Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wip/mongo #261

Merged
merged 24 commits into from
Jul 13, 2020
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
90239ed
*WIP* polydb via Mongoc.jl
benlorenz Jan 22, 2020
11c51bf
basic functionalities added
alexej-jordan Apr 17, 2020
d95cb4e
cleaned up code and improved performance
alexej-jordan Apr 28, 2020
075ab7b
merged master and resolved conflict
alexej-jordan Apr 28, 2020
0de8f8f
added BSONCursor, cleaned up code, improved display of info function
alexej-jordan May 3, 2020
9440d3b
Merge branch 'master' into wip/mongo
alexej-jordan May 3, 2020
8829ac4
can now print available fields for collection; can be improved
alexej-jordan May 10, 2020
0a01806
implemented requested changes; merged Cursor and BSONCursor to common…
alexej-jordan May 10, 2020
1ebbb63
Merge branch 'master' into wip/mongo
alexej-jordan May 10, 2020
d5cb747
polydb: add mozillacacerts_jll for cacert file and set this in the ur…
benlorenz May 13, 2020
32d9dc2
adjustments to printing methods
alexej-jordan May 17, 2020
8c2a704
adjusted the way field names were retrieved
alexej-jordan May 18, 2020
e61f97a
removed commented code
alexej-jordan May 18, 2020
d5686e8
minor changes; commit needed to commit suggestions on github
alexej-jordan May 20, 2020
47a4846
Merge branch 'master' into wip/mongo
alexej-jordan May 20, 2020
53ec24f
Update src/polydb.jl
alexej-jordan May 20, 2020
4067905
Update src/polydb.jl
alexej-jordan May 20, 2020
067659a
Update src/polydb.jl
alexej-jordan May 20, 2020
8d494a9
Update src/polydb.jl
alexej-jordan May 20, 2020
c71c1b2
re-worked workflow for metadata, especially info method
alexej-jordan May 20, 2020
2c77cfe
adjusted comments
alexej-jordan May 20, 2020
80ba02a
Merge branch 'master' into wip/mongo
alexej-jordan May 25, 2020
3b67ce6
added info level option
alexej-jordan May 25, 2020
3a9fbed
info level now also for single collections, minor fixes and jupyter n…
alexej-jordan May 26, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
LoadFlint = "472f376f-f1cf-461b-9ac1-d103423be9b7"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Mongoc = "4fe8b98c-fc19-5c23-8ec2-168ff83495f2"
MozillaCACerts_jll = "14a3606d-f60d-562e-9121-12d972cd8159"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Expand Down
6 changes: 6 additions & 0 deletions deps/rules/julia.rules
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,9 @@ function jupyter_visual_svg(Visual::Object+) {
close $handle;
return $string;
}

function deserialize_json_string($) {
my ($str) = @_;
my $hash = decode_json($str);
return Core::Serializer::deserialize($hash);
}
2 changes: 2 additions & 0 deletions src/Polymake.jl
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,6 @@ include("polynomial.jl")
include("polymake_direct_calls.jl")

include("generate_applications.jl")

include("polydb.jl")
end # of module Polymake
308 changes: 308 additions & 0 deletions src/polydb.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
module Polydb

import Polymake: call_function

using Polymake

# julia itself also has a cert.pem but this one should be more recent
# and provides a variable for the path
using MozillaCACerts_jll

using Mongoc

#Polymake.Polydb's types store information via
# a corresponding Mongoc type variable
struct Database
mdb::Mongoc.Database
end

struct Collection{T}
mcol::Mongoc.Collection
end

struct Cursor{T}
mcursor::Mongoc.Cursor{Mongoc.Collection}
end

# connects to the Polydb and
# returns a Polymake.Polydb.Database instance
function get_db()
# we explicitly set the cacert file, otherwise we might get connection errors because the certificate cannot be validated
client = Mongoc.Client("mongodb://polymake:[email protected]/?authSource=admin&ssl=true&sslCertificateAuthorityFile=$(cacert)")
return Database(client["polydb"])
end

# returns a Polymake.Polydb.Collection instance with the given name
# sections and collections in the name are connected with the '.' sign,
# i.e. names = "Polytopes.Lattice.SmoothReflexive"
Base.getindex(db::Database, name::AbstractString) = Collection{Polymake.BigObject}(db.mdb[name])

# search a collection for documents matching the criteria given by d
function find(c::Collection{T}, d::Dict=Dict(); opts::Union{Nothing, Dict}=nothing) where T
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
return Cursor{T}(Mongoc.find(c.mcol, Mongoc.BSON(d); options=opts))
end

function find(c::Collection{T}, d::Pair...) where T
return Cursor{T}(Mongoc.find(c.mcol, Mongoc.BSON(d...)))
end

# creating `BSON` iterators from the respective `Polymake.BigObject` iterator
function Collection{Mongoc.BSON}(c::Collection{Polymake.BigObject})
return Collection{Mongoc.BSON}(c.mcol)
end

function Cursor{Mongoc.BSON}(cursor::Cursor{Polymake.BigObject})
return Cursor{Mongoc.BSON}(cursor.mcursor)
end

# returns a Polymake.BigObject from a Mongoc.BSON document
function parse_document(bson::Mongoc.BSON)
str = Mongoc.as_json(bson)
return @pm common.deserialize_json_string(str)
end

#Iterator

Base.IteratorSize(::Type{<:Cursor}) = Base.SizeUnknown()
Base.eltype(::Cursor{T}) where T = T

# default iteration functions returning `Polymake.BigObject`s
function Base.iterate(cursor::Polymake.Polydb.Cursor{Polymake.BigObject}, state::Nothing=nothing)
next = iterate(cursor.mcursor, state)
isnothing(next) && return nothing
return Polymake.Polydb.parse_document(first(next)), nothing
end

Base.iterate(coll::Polymake.Polydb.Collection{T}) where T =
return iterate(coll, Cursor{T}(find(coll)))

function Base.iterate(coll::Polymake.Polydb.Collection, state::Polymake.Polydb.Cursor)
next = iterate(state, nothing)
isnothing(next) && return nothing
doc, _ = next
return doc, state
end

# functions for `BSON` iteration
Base.iterate(cursor::Cursor{Mongoc.BSON}, state::Nothing=nothing) =
iterate(cursor.mcursor, state)

Base.iterate(coll::Collection{Mongoc.BSON}) =
iterate(coll.mcol)

Base.iterate(coll::Collection{Mongoc.BSON}, state::Mongoc.Cursor) =
iterate(coll.mcol, state)

#Info

# returns a list of the fields of a collection

function get_fields(coll::Collection)
db = coll.mcol.database
coll_c = db[string("_collectionInfo.", coll.mcol.name)]
info1 = Mongoc.find_one(coll_c, Mongoc.BSON("_id" => "info.2.1"))
info2 = Mongoc.find_one(coll_c, Mongoc.BSON("_id" => info1["schema"]))
schema = info2["schema"]
if haskey(schema, "required")
return Array{String, 1}(schema["required"])
else
return _read_fields(schema)
end
end

function _read_fields(a::Array)
res = Array{String, 1}()
for entry in a
append!(res, _read_fields(entry))
end
return res
end

function _read_fields(d::Dict)
if haskey(d, "required")
return d["required"]
elseif haskey(d, "then")
return _read_fields(d["then"])
else
return _read_fields(d["allOf"])
end
end

function _print_fields(a::Array{Pair{String, String}})
res = ""
for (key, value) in a
if value == ""
res = string(res, key, "\n")
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
else
res = string(res, key, ": ", value, "\n")
end
end
return res
end

# prints information about a specific Collection
# also used for the info(::Database) function
function _info(io::IO, coll::Collection)
db = coll.mcol.database
coll_c = db[string("_collectionInfo.", coll.mcol.name)]
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
info = iterate(coll_c)[1]
print(io, typeof(coll), "\n", _get_collection(info))
end

Base.show(io::IO, coll::Collection) = _info(io, coll)

Base.show(io::IO, ::MIME"text/plain", coll::Collection) = print(io, typeof(coll), ": ", coll.mcol.name)

# returns an array containing the names of all collections in the Polydb, also including meta collections
function _get_collection_names(db::Database)
opts = Mongoc.BSON("authorizedCollections" => true, "nameOnly" => true)
return Mongoc.get_collection_names(db.mdb;options=opts)
end

# returns an array cotaining the names of all collections in the Polydb, excluding meta collections
function get_collection_names(db::Database)
names = _get_collection_names(db)
res = Array{String, 1}()
sizehint!(res, Int64(floor(length(names)/2)))
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
for name in names
if SubString(name, 1, 1) != "_"
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
push!(res, name)
end
end
return res
end

# for the set of names obtained by the _get_collection_names(::Database) function
# returns two arrays containing the names of the meta data collections
# first one for sections, second one for collections
function _get_meta_names(names::Array{String, 1})
n = length(names)
sec_bool = BitArray{1}(undef, n)
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
coll_bool = BitArray{1}(undef, n)
n_secs = 0
n_colls = 0
i = 1
for name in names
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
if SubString(name, 1, 2) == "_s"
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
sec_bool[i] = true
coll_bool[i] = false
n_secs += 1
elseif SubString(name, 1, 2) == "_c"
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
sec_bool[i] = false
coll_bool[i] = true
n_colls += 1
else
sec_bool[i] = false
coll_bool[i] = false
end
i += 1
end
secs = Base.Array{String,1}(undef, n_secs)
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
colls = Base.Array{String,1}(undef, n_colls)
i_s = 1
i_c = 1
for j = 1:n
if sec_bool[j]
secs[i_s] = names[j]
i_s += 1
elseif coll_bool[j]
colls[i_c] = names[j]
i_c += 1
end
end
return secs, colls
end

# functions helping printing metadata for sections or collections
function _get_contact(s::String)
return s
end

function _get_contact(a::Array)
res = ""
for dict in a
str = dict["name"]
for key in ["email", "www", "affiliation"]
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
if !isempty(get(dict, key, ""))
str = string(str, ", ", dict[key])
end
end
res = string(res, "\t\t", str, "\n")
end
return res
end

# prints information about a specific section and
# continues to print information about its content
function _get_section(db::Database, info::Mongoc.BSON, sections::Array{String,1}, collections::Array{String,1})
res = string("SECTION: ", join(info["section"], "."), "\n", info["description"], "\n")
if haskey(info, "maintainer")
res = string(res, "Maintained by ", info["maintainer"]["name"], ", ", info["maintainer"]["email"], ", ", info["maintainer"]["affiliation"], "\n")
end
return string(res, "\n", _get_sections(db, info["section"], sections, collections))
end

# prints information about a specific collection
function _get_collection(info::Mongoc.BSON)
res = string("\tCOLLECTION: ", join(info["section"], "."), ".", info["collection"], "\n")
if haskey(info, "description")
res = string(res, "\t", info["description"], "\n")
end
if haskey(info, "author")
res = string(res, "\tAuthored by ", "\n", _get_contact(info["author"]))
end
if haskey(info, "maintainer")
res = string(res, "\tMaintained by", "\n", _get_contact(info["maintainer"]))
end
return res
end

# initializes printing complete section/collection tree
function _get_sections(db::Database, sections::Array{String,1}, collections::Array{String,1})
res = ""
for sec in sections
sec_c = db.mdb[sec]
info = iterate(sec_c)[1]
if length(info["section"]) == 1
res = string(res, _get_section(db, info, sections, collections))
alexej-jordan marked this conversation as resolved.
Show resolved Hide resolved
end
end
return res
end

# prints subsections/collection tree of a section given by the array s
# i.e. for the section "Polytopes.Lattice", s = ["Polytopes", "Lattice"]
function _get_sections(db::Database, s::Array{Any,1}, sections::Array{String,1}, collections::Array{String,1})
res = ""
coll_bool = true
for sec in sections
sec_c = db.mdb[sec]
info = iterate(sec_c)[1]
if length(info["section"]) == length(s) + 1 && info["section"][1:length(s)] == s
coll_bool = false
res = string(res, _get_section(db, info, sections, collections))
end
end
# as of now, each section either contains subsections or collections
if coll_bool
for coll in collections
coll_c = db.mdb[coll]
info = iterate(coll_c)[1]
if info["section"] == s
res = string(res, _get_collection(info), "\n")
end
end
end
return res
end

# prints a sorted list of the sections and collections of the Polydb
# together with information about each of these, if existent
# relying on the structure of Polydb
function info(db::Database)
names = _get_collection_names(db)
sections, collections = _get_meta_names(names)
println(_get_sections(db, sections, collections))
end

end