I would store the graph as a directed graph, which allows you to use queries more effectively. Obviously you need to have the constraint that all directed edges must have a partnering edge going in the opposite direction.
Class Vertex(db.Model):
#Vertex specific stuff
Class Edge(db.Model):
Start = db.ReferenceProperty(Vertex)
End = db.ReferenceProperty(Vertex)
You can then pull out all of the edges relating to a specific vertex with a simple query:
#getting all neighbours of a vertex called "foo"
Neighbours = Edge.all()
Neighbours.filter("Start = ", foo)
#neighbours now contains a set of all edges leading from foo
Nice and simple, taking advantage of the fact you're using appengine so you can let indexing do a lot of the work for you ;)
If you want to make sure that directed constraint remains true, obviously use a method to create edges like so:
LinkVertices(A, B) #A and B are vertices
edgeOne = Edge()
edgeOne.Start = A
edgeOne.End = B
edgeOne.put()
edgeTwo = Edge()
edgeTwo.Start = B
edgeTwo.End = A
edgeTwo.put()
Addressing roffles concerns about storing all the edges twice, you could try something like this:
Class Edge(db.Model):
A = db.ReferenceProperty(Vertex)
B = db.ReferenceProperty(Vertex)
#getting all neighbours of a vertex called "foo"
Neighbours = Edge.all()
Neighbours.filter("A = ", foo)
OtherNeighbours = Edge.all()
OtherNeighbours.filter("B = ", foo)
#these two queries now contains all edges leading from foo.
This approach basically saves you storage space (every edge is only stored once) at the cost of slightly higher query times and much messier code. In my opinion that's not a very good tradeoff, since you're saving yourself about 64 bytes storage per edge.