Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
edmondchuc committed Aug 16, 2020
0 parents commit 5ac254c
Show file tree
Hide file tree
Showing 16 changed files with 772 additions and 0 deletions.
115 changes: 115 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@

# Created by https://www.toptal.com/developers/gitignore/api/intellij+all,go
# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,go

### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

### Go Patch ###
/vendor/
/Godeps/

### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml

# Gradle
.idea/**/gradle.xml
.idea/**/libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

### Intellij+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360

.idea/

# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023

*.iml
modules.xml
.idea/misc.xml
*.ipr

# Sonarlint plugin
.idea/sonarlint

# End of https://www.toptal.com/developers/gitignore/api/intellij+all,go
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# gordf

The gordf module is an RDF library for Go.

## Current features
- Parse N-Triples from file
- Naive memory graph data structure
- Idiomatic querying of the memory graph
- Structs to represent URI and literals (both typed and lang tags)

## Planned features
- Blank Nodes
- Parsing other RDF formats such as Turtle
- Serialise to disk in RDF formats (N-Triple, Turtle, etc)
- Idiomatic representation of CURIES E.g. `rdf:type` -> `RDF{l: "type"}`

## Example usage
```go
package main

import (
"fmt"
rdf "gordf/gordf"
"gordf/parser"
)

func main() {
g := rdf.Graph{}

parser.ParseFile(&g, "data.nt", "nt")

rdfType := rdf.NewURI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
schemaName := rdf.NewURI("http://schema.org/name")

for triple := range g.Triples(rdf.None{}, rdfType, rdf.NewURI("http://schema.org/Organization")) {
for triple := range g.Triples(triple.S, schemaName, rdf.None{}) {
fmt.Println(triple)
}
}
}
```
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module gordf

go 1.15

require github.com/iand/ntriples v0.0.0-20170827115059-bd9c67713a4a
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/iand/ntriples v0.0.0-20170827115059-bd9c67713a4a h1:k72398bGJXVOPPITdRiexxvB//2NwoEjgR+f58/qyps=
github.com/iand/ntriples v0.0.0-20170827115059-bd9c67713a4a/go.mod h1:mVRj8HGAAuBOqqY3RaUzs2B7leqqoKx/L9KD9mUrnKo=
1 change: 1 addition & 0 deletions gordf/bnode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package gordf
140 changes: 140 additions & 0 deletions gordf/graph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package gordf

import (
"gordf/set"
)

// A Graph struct with three indexes of the different possible triple permutations for fast query access.
type Graph struct {
spo map[Node]map[Node]set.Set
pos map[Node]map[Node]set.Set
osp map[Node]map[Node]set.Set
}

func (g *Graph) Add(s, p, o Node) {
addToIndex(&g.spo, s, p, o)
addToIndex(&g.pos, p, o, s)
addToIndex(&g.osp, o, s, p)
}

func addToIndex(index *map[Node]map[Node]set.Set, a, b, c Node) {
// Initialise the current index if it is nil.
if *index == nil {
*index = make(map[Node]map[Node]set.Set)
}

// If a not in index
if _, ok := (*index)[a]; !ok {
(*index)[a] = make(map[Node]set.Set)
s := set.Set{}
s.Add(c)
(*index)[a][b] = s
} else {
// if b not in index
if _, ok := (*index)[a][b]; !ok {
s := set.Set{}
s.Add(c)
(*index)[a][b] = s
} else {
s := (*index)[a][b]
s.Add(c)
}
}
}

func (g *Graph) Triples(s, p, o Node) chan Triple {
triples := make(chan Triple)

go func() {
// if s is not None
if _, ok := s.(None); !ok {
// if P is not None
if _, ok := p.(None); !ok {
// if O is not None
// sub pred obj
if _, ok := o.(None); !ok {
// if O in spo index
if (*g).spo[s][p].Exists(o) {
triples <- Triple{S: s, P: p, O: o}
}
} else {
// sub pred None
retObjects := (*g).spo[s][p]
for retObj := range retObjects.Range() {
retObjNode, _ := retObj.(Node)
triples <- Triple{S: s, P: p, O: retObjNode}
}
}
} else {
// sub None obj
if _, ok := o.(None); !ok {
retPredicates := (*g).osp[o][s]
for retPred := range retPredicates.Range() {
retPredNode, _ := retPred.(Node)
triples <- Triple{S: s, P: retPredNode, O: o}
}
} else {
// sub None None
retPredicates := (*g).spo[s]
for retPred := range retPredicates {
retObjects := (*g).spo[s][retPred]
for retObj := range retObjects.Range() {
retObjNode, _ := retObj.(Node)
triples <- Triple{S: s, P: retPred, O: retObjNode}
}
}
}
}
} else {
if _, ok := p.(None); !ok {
// None pred obj
if _, ok := o.(None); !ok {
retSubjects := (*g).pos[p][o]
for retSub := range retSubjects.Range() {
retSubNode, _ := retSub.(Node)
triples <- Triple{S: retSubNode, P: p, O: o}
}
} else {
// None pred None
retObjects := (*g).pos[p]
for retObj := range retObjects {
retSubjects := (*g).pos[p][retObj]
for retSub := range retSubjects.Range() {
retSubNode, _ := retSub.(Node)
triples <- Triple{S: retSubNode, P: p, O: retObj}
}
}
}
} else {
// None None obj
if _, ok := o.(None); !ok {
retSubjects := (*g).osp[o]
for retSub := range retSubjects {
retPredicates := (*g).osp[o][retSub]
for retPred := range retPredicates.Range() {
retPredNode, _ := retPred.(Node)
triples <- Triple{S: retSub, P: retPredNode, O: o}
}
}
} else {
// None None None
retSubjects := (*g).spo
for retSub := range retSubjects {
retPredicates := (*g).spo[retSub]
for retPred := range retPredicates {
retObjects := (*g).spo[retSub][retPred]
for retObj := range retObjects.Range() {
retObjNode, _ := retObj.(Node)
triples <- Triple{S: retSub, P: retPred, O: retObjNode}
}
}
}
}
}
}

close(triples)
}()

return triples
}
72 changes: 72 additions & 0 deletions gordf/literal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package gordf

import "fmt"

// The Literal struct implements the Node interface.
type Literal struct {
value interface{}
datatype URI
language string
}

func (literal Literal) Equals(n Node) bool {
literal2, ok := n.(Literal)
if ok {
return literal.value == literal2.value
}
return false
}

func (literal Literal) Value() string {
return fmt.Sprintf("%v", literal.value)
}

func (literal Literal) String() string {

// Literal is a number.
// TODO: Check if checking these types is enough for numbers. I think just checking int is enough
// as it should be the set of all ints, but not 100% sure.
_, okInt := literal.value.(int)
_, okFloat32 := literal.value.(float32)
_, okFloat64 := literal.value.(float64)
if okInt || okFloat32 || okFloat64 {
return fmt.Sprintf(`%v`, literal.value)
}

// Literal is a bool.
_, ok := literal.value.(bool)
if ok {
return fmt.Sprintf(`"%v"^^<http://www.w3.org/2001/XMLSchema#boolean>`, literal.value)
}

// Literal is a string with a language tag and a datatype.
if literal.language != "" && literal.datatype.value != "" {
return fmt.Sprintf(`"%v"@%v^^%v`, literal.value, literal.language, literal.datatype)
}

// Literal is a string and has either a language tag or datatype.
if literal.datatype.value != "" {
return fmt.Sprintf(`"%v"^^%v`, literal.value, literal.datatype)
} else if literal.language != "" {
return fmt.Sprintf(`"%v"@%v`, literal.value, literal.language)
}

// Literal is a string and has no language tag or datatype.
return fmt.Sprintf(`"%v"`, literal.value)
}

func NewLiteralWithLanguageAndDatatype(value string, datatype URI, language string) Literal {
return Literal{value: value, datatype: datatype, language: language}
}

func NewLiteralWithDatatype(value string, datatype URI) Literal {
return Literal{value: value, datatype: datatype}
}

func NewLiteralWithLanguage(value string, language string) Literal {
return Literal{value: value, language: language}
}

func NewLiteral(value interface{}) Literal {
return Literal{value: value}
}
Loading

0 comments on commit 5ac254c

Please sign in to comment.