foundationdb/recipes/java-recipes/MicroGraph.java

216 lines
6.7 KiB
Java

/*
* MicroGraph.java
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* 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.
*/
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import com.foundationdb.*;
import com.foundationdb.async.Function;
import com.foundationdb.subspace.Subspace;
import com.foundationdb.tuple.Tuple;
public class MicroGraph {
private static final FDB fdb;
private static final Database db;
private static final Subspace graph;
private static final Subspace edge;
private static final Subspace inverse;
private static final Subspace pathTwo;
private static final Subspace wEdge;
private static final Subspace wInverse;
static {
fdb = FDB.selectAPIVersion(300);
db = fdb.open();
graph = new Subspace(Tuple.from("G"));
edge = graph.subspace(Tuple.from("E"));
inverse = graph.subspace(Tuple.from("I"));
pathTwo = graph.subspace(Tuple.from("P"));
wEdge = graph.subspace(Tuple.from("WE"));
wInverse = graph.subspace(Tuple.from("WI"));
}
// TODO These next five methods (setEdge, deleteEdge, getOutNeighbors, getInNeighbors,
// and getNeighbors) are all in the recipe book.
public static void setEdge(TransactionContext tcx, final String node, final String neighbor){
tcx.run(new Function<Transaction,Void>() {
public Void apply(Transaction tr){
tr.set(edge.pack(Tuple.from(node,neighbor)), Tuple.from().pack());
tr.set(inverse.pack(Tuple.from(neighbor,node)), Tuple.from().pack());
return null;
}
});
}
public static void deleteEdge(TransactionContext tcx, final String node, final String neighbor){
tcx.run(new Function<Transaction,Void>() {
public Void apply(Transaction tr){
tr.clear(edge.pack(Tuple.from(node,neighbor)));
tr.clear(inverse.pack(Tuple.from(neighbor,node)));
return null;
}
});
}
public static ArrayList<String> getOutNeighbors(TransactionContext tcx, final String node){
return getNeighbors(tcx, node, edge);
}
public static ArrayList<String> getInNeighbors(TransactionContext tcx, final String node){
return getNeighbors(tcx, node, inverse);
}
private static ArrayList<String> getNeighbors(TransactionContext tcx, final String node, final Subspace domain){
return tcx.run(new Function<Transaction,ArrayList<String> >(){
public ArrayList<String> apply(Transaction tr){
ArrayList<String> neighbors = new ArrayList<String>();
for(KeyValue kv : tr.getRange(
domain.subspace(Tuple.from(node)).range())){
neighbors.add(domain.unpack(kv.getKey()).getString(1));
}
return neighbors;
}
});
}
// TODO These next two methods contain code that is referenced
// in the extensions of graph, namely code to handle weighted graphs.
// The third is not mentioned but is a necessary add on to read weights.
public static void setEdgeWeighted(TransactionContext tcx, final String node, final String neighbor, final long weight){
tcx.run(new Function<Transaction,Void>(){
public Void apply(Transaction tr){
ByteBuffer b = ByteBuffer.allocate(8);
b.order(ByteOrder.LITTLE_ENDIAN);
b.putLong(weight);
tr.set(wEdge.pack(Tuple.from(node,neighbor)), b.array());
tr.set(wInverse.pack(Tuple.from(neighbor,node)), b.array());
return null;
}
});
}
public static void updateEdgeWeight(TransactionContext tcx, final String node, final String neighbor, final long change){
tcx.run(new Function<Transaction,Void>() {
public Void apply(Transaction tr){
ByteBuffer b = ByteBuffer.allocate(8);
b.order(ByteOrder.LITTLE_ENDIAN);
b.putLong(change);
tr.mutate(MutationType.ADD, wEdge.pack(Tuple.from(node,neighbor)), b.array());
tr.mutate(MutationType.ADD, wInverse.pack(Tuple.from(neighbor,node)), b.array());
return null;
}
});
}
public static long getWeight(TransactionContext tcx, final String node, final String neighbor){
return tcx.run(new Function<Transaction,Long>() {
public Long apply(Transaction tr){
ByteBuffer b = ByteBuffer.allocate(8);
b.order(ByteOrder.LITTLE_ENDIAN);
b.put(tr.get(wEdge.pack(Tuple.from(node,neighbor))).get());
return b.getLong(0);
}
});
}
public static void setNeighborNeighbors(TransactionContext tcx, final String node, final String neighbor, final String neighborsNeighbor){
tcx.run(new Function<Transaction,Void>() {
public Void apply(Transaction tr){
// TODO These two lines are the code in the recipe book to handle
// the addition of a "pathTwo" subspace that would handle remembering
// where all the neighbor's neighbors are.
tr.set(edge.pack(Tuple.from(node,neighbor)),Tuple.from().pack());
tr.set(pathTwo.pack(Tuple.from(node,neighbor)),Tuple.from().pack());
return null;
}
});
}
public static void clearSubspace(TransactionContext tcx, final Subspace s){
tcx.run(new Function<Transaction,Void>() {
public Void apply(Transaction tr){
//tr.clear(Range.startsWith(s.getKey()));
tr.clear(s.range());
return null;
}
});
}
public static void smokeTest(){
/*
* A simple directed graph:
*
* A <-> B -> C
* ^
* |
* D <- E
*/
setEdge(db,"A","B");
setEdge(db,"B","A");
setEdge(db,"B","C");
setEdge(db,"D","B");
setEdge(db,"E","D");
String[] nodes = {"A","B","C","D","E"};
for(String node : nodes){
System.out.println(node + " "
+ getOutNeighbors(db,node).toString() + " "
+ getInNeighbors(db,node).toString());
}
/*
* Simple weighted graph.
*
* A -4-> B <-2- C
* ^
* 3
* |
* D -(-1)-> E
*/
setEdgeWeighted(db,"A","B",4l);
setEdgeWeighted(db,"C","B",2l);
setEdgeWeighted(db,"D","B",3l);
setEdgeWeighted(db,"D","E",-1l);
System.out.println(getWeight(db,"A","B"));
System.out.println(getWeight(db,"C","B"));
System.out.println(getWeight(db,"D","B"));
System.out.println(getWeight(db,"D","E"));
updateEdgeWeight(db,"D","B",27l);
System.out.println(getWeight(db,"A","B"));
System.out.println(getWeight(db,"C","B"));
System.out.println(getWeight(db,"D","B"));
System.out.println(getWeight(db,"D","E"));
}
public static void main(String[] args) {
clearSubspace(db, graph);
smokeTest();
}
}