File: README.md
Cayley is an open-source database for Linked Data. It is inspired by the graph database behind Google's Knowledge Graph (formerly Freebase).
[Documentation](https://cayley.gitbook.io/cayley/)
Features
- Built-in query editor, visualizer and REPL
- Multiple query languages:
- Modular: easy to connect to your favorite programming languages and back-end stores
- Production ready: well tested and used by various companies for their production workloads
- Fast: optimized specifically for usage in applications
Performance
Rough performance testing shows that, on 2014 consumer hardware and an average disk, 134m quads in LevelDB is no problem and a multi-hop intersection query -- films starring X and Y -- takes ~150ms.
Community
- Website: cayley.io
- Slack: cayleygraph.slack.com -- Invite here
- Discourse list: discourse.cayley.io (Also acts as mailing list, enable mailing list mode)
- Twitter: @cayleygraph
- Involvement: Contribute
File: appengine/README.md
About appengine
This directory contains a appengine deployable cayley graph instance.
Running/testing traditional appengine locally
Install the latest appengine sdk, and ensure that your local version of go
matches the same version obtained from goapp.
Note: if you use a more recent version of Go locally, goapp get might
accidentally copy the wrong files from the local $GOPATH.
$ cd $GOPATH/src/github.com/cayleygraph/cayley/appengine
# check go version
$ go version
# check goapp version
$ goapp version
# install dependencies
$ goapp get
# ensure that goapp can build
$ goapp build
# test locally
$ goapp serveRunning/testing flexible appengine locally
The latest appengine flexible environment can be tested instead of the
traditional environment by doing the following:
$ go generate
$ goapp serve app.flexible.yamlDeploying to traditional appengine environment
$ goapp deploy -version 1 -application <MY_PROJECT_ID>Deploying to flexible appengine environment
# install aedeploy if not installed
$ go get -u google.golang.org/appengine/cmd/aedeploy
$ go generate
# deploy
$ aedeploy gcloud app deploy app.flexible.yamlFile: docs/deployment/container.md
Running in Docker
Running in Kubernetes
To run Cayley in K8S check this docs section.
Running in a container
A container exposing the HTTP API of Cayley is available.
Running with default configuration
Container is configured to use BoltDB as a backend by default.
docker run -p 64210:64210 -d ghcr.io/cayleygraph/cayleyNew database will be available at http://localhost:64210.
Custom configuration
To run the container one must first setup a data directory that contains the configuration file and optionally contains persistent files (i.e. a boltdb database file).
mkdir data
cp cayley_example.yml data/cayley.yml
cp data/testdata.nq data/my_data.nq
# initialize and serve database
docker run -v $PWD/data:/data -p 64210:64210 -d ghcr.io/cayleygraph/cayley -c /data/cayley.yml --init -i /data/my_data.nq
# serve existing database
docker run -v $PWD/data:/data -p 64210:64210 -d ghcr.io/cayleygraph/cayley -c /data/cayley.ymlOther commands
Container runs cayley http command by default. To run any other Cayley command reset the entry point for container:
docker run -v $PWD/data:/data ghcr.io/cayleygraph/cayley --entrypoint=cayley versionFile: docs/deployment/k8s-1.md
Running in Kubernetes
Most examples requires Kubernetes 1.5+ and PersistentVolumes are configured.
After running scripts namespace cayley will be created and service with the same name will be available in cluster. Service is of type ClusterIP by default. If you want to expose it, consider changing type to LoadBalancer.
Single instance \(Bolt\)
This is a simplest possible configuration: single Cayley instance with persistent storage, using Bolt as a backend.
kubectl create -f ./docs/k8s/cayley-single.ymlDistributed \(MongoDB cluster\)
This example is based on thesandlord/mongo-k8s-sidecar and runs Cayley on top of 3-node Mongo cluster.
kubectl create -f ./docs/k8s/cayley-mongo.ymlDistributed
TODO: PostgreSQL, CockroachDB
File: docs/getting-involved/contributing.md
Contributing
Contributing
Community Involvement
Join our community on discourse.cayley.io or other Locations.
Simply building Cayley
If your version of Go < 1.13, you need to run:
export GO111MODULE=onFollow the instructions for running Cayley locally:
# clone project
git clone https://github.com/cayleygraph/cayley
cd cayley
# Download dependencies
go mod download
# Update web files (optional)
go run cmd/download_ui/download_ui.goGenerate static files go modules
packr2
build the binary
go build ./cmd/cayley
try the generated binary
./cayley helpGive it a quick test with:
./cayley repl -i data/testdata.nqTo run the web frontend, replace the "repl" command with "http"
./cayley http -i data/testdata.nqYou can now open the WebUI in your browser: http://127.0.0.1:64210
Hacking on Cayley
First, you'll need Go (version 1.11.x or greater) and a Go workspace. This is outlined by the Go team at http://golang.org/doc/code.html and is sort of the official way of going about it.
If your version of Go < 1.13, you need to run:
export GO111MODULE=onIf you just want to build Cayley and check out the source, or use it as a library, a simple go get github.com/cayleygraph/cayley will work!
But suppose you want to contribute back on your own fork (and pull requests are welcome!). A good way to do this is to set up your $GOPATH and then...
mkdir -p $GOPATH/src/github.com/cayleygraph
cd $GOPATH/src/github.com/cayleygraph
git clone https://github.com/$GITHUBUSERNAME/cayley...where $GITHUBUSERNAME is, well, your GitHub username :) You'll probably want to add
cd cayley
git remote add upstream http://github.com/cayleygraph/cayleySo that you can keep up with the latest changes by periodically running
git pull --rebase upstreamWith that in place, that folder will reflect your local fork, be able to take changes from the official fork, and build in the Go style.
For iterating, it can be helpful to, from the directory, run
packr2 && go build ./cmd/cayley && ./cayley <subcommand> <your options>Which will also resolve the relevant static content paths for serving HTTP.
Reminder: add yourself to CONTRIBUTORS and AUTHORS.
Running Unit Tests
If your version of Go < 1.13, you need to run:
export GO111MODULE=onFirst, cd into the cayley project folder and run:
go test ./...If you have a Docker installed, you can also run tests for remote backend implementations:
go test -tags docker ./...If you have a Docker installed, you only want to run tests for a specific backend implementations eg. mongodb
go test -tags docker ./graph/nosql/mongoIntegration tests can be enabled with environment variable:
RUN_INTEGRATION=true go test ./...File: docs/getting-involved/glossary.md
Glossary of Terms
Note: this definitions in this glossary are sequenced so that they build on each other, one to the next, rather than alphabetically.
triple
- a data entity composed of subject-predicate-object, like "Bob is 35" or "Bob knows Fred". (A predicate in traditional grammar...is seen as a property that a subject has or is characterized by.) source and [source](https://en.wikipedia.org/wiki/Predicate_(grammar)#Predicates_in_traditional_grammar)
triplestore
- a purpose-built database for the storage and retrieval of triples... source
quad
where triples have the form
{subject, predicate, object}, quads would have a form along the lines of{subject, predicate, object, context}sourceYou can add context or extra values to triples that identifies them and makes it easy to define subgraphs, or named properties. source
From Cayley godoc:
gotype Quad struct { Subject Value `json:βsubjectβ` Predicate Value `json:βpredicateβ` Object Value `json:βobjectβ` Label Value `json:βlabel,omitemptyβ` }
link
- Another name for a triple, since it "links" any two nodes.
- Given the triple
{A, knows, C}you would say in graph terminology thatAandCare "vertices" whileknowsis an "edge". You would also say thatA,knows, andCare all "nodes", and they are "linked" to one another by the triple.
IRI
- IRI is an RDF Internationalized Resource Identifier. source
- An IRI (Internationalized Resource Identifier) within an RDF graph is a Unicode string that conforms to the syntax defined in RFC 3987. source
- IRIs are a generalization of URIs that permits a wider range of Unicode characters. Every absolute URI and URL is an IRI, but not every IRI is an URI. source
RDF
- Resource Description Framework, basically a set of standards defined around quads
- An RDF triple consists of three components:
- the subject, which is an IRI or a blank node
- the predicate, which is an IRI
- the object, which is an IRI, a literal or a blank node source
RDF store, quad store, named graph, semantic graph database
- ...persisting RDF β storing it β became a thing, and these stores were called triple stores. Next they were called quad stores and included information about context and named graphs, then RDF stores, and most recently they call themselves βsemantic graph database.β source
- Adding a name to the triple makes a "quad store" or named graph. source
Cayley
- Cayley is a quad store that supports multiple storage backends. It supports multiple query languages for traversing and filtering the named graphs formed by its quads, and it has associated tooling such as a CLI, HTTP server, and so on.
Gizmo
- A Gremlin/TinkerPop-inspired query language for Cayley. Looks a lot like JavaScript, the syntax is documented here.
g.V\(\)
- For Gremlin/TinkerPop, g.V() returns a list of all the vertices in the graph
.v()is for "Vertex" in Gizmo, and it is used likepathObject = graph.Vertex([nodeId],[nodeId]...)(see [[path|#path]])
inbound/outbound predicate
Inbound/outbound refers to the direction of a relation via a predicate. In the case of the triple "A follows B", "follows" is an outbound predicate for
Aand an inbound predicate forB.In/out predicates can be expressed in a query language, for example using the format
resultSet = subject.out(predicate)to discover matchingObjects. In the case of the triple "A follows B",A.out(βfollowsβ)would return a set of nodes which containsB. An excellent example of this sort of query format is given in the Gremlin/TinkerPop homepage example:javascriptWhat are the names of projects that were created by two friends? g.V().match( as(βaβ).out(βknowsβ).as(βbβ), as(βaβ).out(βcreatedβ).as(βcβ), as(βbβ).out(βcreatedβ).as(βcβ), as(βcβ).in(βcreatedβ).count().is(2)). select(βcβ).by(βnameβ)
direction
Direction specifies a node's position within a quad. source
goconst ( Any Direction = iota Subject Predicate Object Label )Direction is passed to the
Getmethod of a quad to access one of its four parts, see quad.Get(d Direction) ValueThe term "Direction" comes about from the concept of traversing a graph. Take for example the triple
{A, follows, B}and supposing you "select" the predicatefollows. Now you want to traverse the graph, so you move in theObjectdirection, and you now haveBselected. Whereas the high-level path abstraction for queries uses inbound/outbound predicates to represent movement on the graph, the bottom-level iterator mechanic uses Direction.
path
Paths are just a set of helpers to build a query, but they are not that good for building something more complex. You can try using Shapes for this - it will give you a full control of what the query actually does. source
Path represents either a morphism (a pre-defined path stored for later use), or a concrete path, consisting of a morphism and an underlying QuadStore. source
Underlying code:
gotype Path struct { stack []morphism qs graph.QuadStore baseContext pathContext } type morphism struct { IsTag bool Reversal func(*pathContext) (morphism, *pathContext) Apply applyMorphism tags []string } type applyMorphism func(shape.Shape, *pathContext) (shape.Shape, *pathContext)So, as previously stated, the path package is just helper methods on top of the shape package.
morphism
Morphism is basically a path that is not attached to any particular quadstore or a particular starting point in the graph. Morphisms are meant to be used as a query part that can be applied to other queries to follow a path specified in the Morphism.
A good example will be a
FollowRecursivefunction that will apply a single morphism multiple times to get to all nodes that can be traversed recursively. source
iterator
So a graph query is roughly represented as a tree of iterators β things
that implement graph.Iterator. An iterator is (loosely) a stand-in for a
set of things that match a particular portion of the graph. source
subiterator
- So a graph query is roughly represented as a tree of iterators...Evaluation is merely calling Next() repeatedly on the iterator at the top of the tree. Subiterators, then, are the branches and leaves of the tree. source
- Example of converting the Cayley-Gremlin-Go-API query
g.V(βBβ).In(βfollowsβ).All()into an iterator tree:- HasA (subject) β gets the things in the subject field for:
- And β the intersection of:
- LinksTo (predicate) links that have the predicate ofβ¦:
- Fixed iterator containing βfollowsβ β β¦ just the node βfollowsβ.
- LinksTo (object) links that have the object field of:
- Fixed iterator containing βBβ β β¦ just the node βBβ
- LinksTo (predicate) links that have the predicate ofβ¦:
- And β the intersection of:
- HasA (subject) β gets the things in the subject field for:
LinkTo iterator
- A LinksTo takes a subiterator of nodes, and contains an iteration of links which "link to" those nodes in a given direction. ... Can be seen as the dual of the HasA iterator. source
- Next()ing a LinksTo is straightforward -- iterate through all links to things in the subiterator, and then advance the subiterator, and do it again.
To restate in pseudo-code;
resultsis what would be returned in successiveNext()calls:govar results []quad.Quad for _, node := range linkTo.subIterator { for _, quad := range allQuads { if quad.Get(linkTo.direction) == node { results = append(results, quad) } } }
- Contains()ing a LinksTo means, given a link, take the direction we care about and check if it's in our subiterator.
To restate in pseudo-code:
gofor _, node := range linkTo.subIterator { if theLink.Get(linkTo.direction) == node { return true } } return false
- Next()ing a LinksTo is straightforward -- iterate through all links to things in the subiterator, and then advance the subiterator, and do it again.
HasA iterator
- The HasA takes a subiterator of links, and acts as an iterator of nodes in the given direction. The name comes from the idea that a "link HasA subject" or a "link HasA predicate". source
-
go
var results []quad.Value for _, quad := range hasA.subIterator { results = append(results, quad.Get(hasA.direction)) } Contains()
gofor _, quad := range hasA.subIterator { if quad.Get(hasA.direction) == theNode { return true } } return false
-
shape
Shape represent a query tree shape. source
gotype Shape interface { BuildIterator(qs graph.QuadStore) graph.Iterator Optimize(r Optimizer) (Shape, bool) }This is the most interesting part of the query system - it describes how exactly the query looks like. ... This package also describes different query optimizations that are not specific to a backend. ... You can write a query using either Paths, Shapes or raw Iterators... source
A Shape seems to be an abstract representation of a query, a level above Iterators and a level below Paths. You can perform various operations on it (traverse inbound/outbound predicates, find unions and intersections, etc.) and most importantly build a tree of Iterators from it, which will do the mechanical act of processing quads to find results.
token
In the context of a quad store, a graph.Value. However the backend wishes to implement it, a Value is merely a token to a quad or a node that the backing store itself understands, and the base iterators pass around.
For example, in a very traditional, graphd-style graph, these are int64s (guids of the primitives). In a very direct sort of graph, these could be pointers to structs, or merely quads, or whatever works best for the backing store.
reification
βWith reification, we create a metagraph on top of our graph that represents the statement that we have here. We create a new node that represents a statement and points at the subject...β source
Reifying a relationship means viewing it as an entity. The purpose of reifying a relationship is to make it explicit, when additional information needs to be added to it.
Viewing a relationship as an entity, one can say that the entity reifies the relationship. This is called reification of a relationship. Like any other entity, it must be an instance of an entity type. source
File: docs/getting-involved/locations.md
Locations of parts of Cayley
Community
- Where is the beating heart of this community? https://discourse.cayley.io/
- Where is the mailing list? https://discourse.cayley.io/ (enable mailing list mode under options)
- Where is the chat room? cayleygraph.slack.com -- Invite here
- Where should I start contributing? Contributing.md (Also: How to get involved!)
- Where is the site-content? https://github.com/cayleygraph/site-content
- Where are issues? https://github.com/cayleygraph/cayley/issues
- Where is the graph source code? https://github.com/cayleygraph/cayley
- Where are pull requests? https://github.com/cayleygraph/cayley/pulls
- Where is the wiki? It was at https://github.com/cayleygraph/cayley/wiki, but we are deprecating it.
- Where is the (old) mailing list? It was at https://groups.google.com/forum/#!forum/cayley-users, but we are deprecating it for this forum (and its mailing list mode).
File: docs/getting-involved/todo.md
TODOs
The main source of our TODO list is our Github Issues, so we are going to try to avoid duplicating them here.
Anything marked "TODO" in the code.
Usually something that should be taken care of.
File: docs/k8s/k8s.md
Running in Kubernetes
Most examples requires Kubernetes 1.5+ and PersistentVolumes are configured.
After running scripts namespace cayley will be created and service with the same name will be available in cluster. Service is of type ClusterIP by default. If you want to expose it, consider changing type to LoadBalancer.
Single instance \(Bolt\)
This is a simplest possible configuration: single Cayley instance with persistent storage, using Bolt as a backend.
kubectl create -f ./docs/k8s/cayley-single.ymlDistributed \(MongoDB cluster\)
This example is based on thesandlord/mongo-k8s-sidecar and runs Cayley on top of 3-node Mongo cluster.
kubectl create -f ./docs/k8s/cayley-mongo.ymlDistributed
TODO: PostgreSQL, CockroachDB