Welcome to pydiggy

Okay everyone … so I am still VERY new to using Dgraph. But, I wanted to be able to turn a python object …

class Region(Node):
    area: int
    population: int
    name: str
    borders: List[Region]

into a schema …

Region: bool @index(bool) .
_type: string .
area: int .
borders: uid .
name: string .
population: int .

Hmm … while I am at it, maybe we should also generate the mutations:

from pydiggy import generate_mutation, Facets

por = Region(uid=0x11, name="Portugal")
spa = Region(uid=0x12, name="Spain")
gas = Region(name="Gascony")
mar = Region(name="Marseilles")

por.borders = [spa]
spa.borders = [por, gas, mar]
gas.borders = [Facets(spa, foo='bar', hello='world'), mar]
mar.borders = [spa, gas]

por.stage()
spa.stage()
gas.stage()
mar.stage()

print(generate_mutation())

Prints out

{
    set {
    <0x11> <Region> "true" .
    <0x11> <_type> "Region" .
    <0x11> <name> "Portugal" .
    <0x11> <borders> <0x12> .
    <0x12> <Region> "true" .
    <0x12> <_type> "Region" .
    <0x12> <name> "Spain" .
    <0x12> <borders> <0x11> .
    <0x12> <borders> _:unsaved.0 .
    <0x12> <borders> _:unsaved.1 .
    _:unsaved.0 <Region> "true" .
    _:unsaved.0 <_type> "Region" .
    _:unsaved.0 <name> "Gascony" .
    _:unsaved.0 <borders> <0x12> (foo="bar", hello="world") .
    _:unsaved.0 <borders> _:unsaved.1 .
    _:unsaved.1 <Region> "true" .
    _:unsaved.1 <_type> "Region" .
    _:unsaved.1 <name> "Marseilles" .
    _:unsaved.1 <borders> <0x12> .
    _:unsaved.1 <borders> _:unsaved.0 .
    }
}

Maybe also go the other way. A Dgraph result into python objects.

>>> data = hydrate(retrieved_data)

{'allRegions': [<Region:17>, <Region:18>, <Region:19>, <Region:20>]}

Now, this is in no way production worthy. More a proof of concept at this stage. And, since I am still new to Dgraph, I am not even sure if I am following best practices yet. Still plenty of work to go, but any thoughts on what I should or should not be doing would be welcome.

5 Likes

This seems very cool!

1 Like

:grin: That was my thought when I came across Dgraph.

1 Like

As an python and golang programmer, this project is awesome.

Thanks :smiley:

I’m really interested to hear what others have to say about having both of these being autogenerated on every object:

Region: bool @index(bool) .
_type: string .

The _type is there so that we can tell what class to deserialize to. But, from my understanding, it is much more efficient to do queries like has(Region) versus eq(_type, 'Region'). So, for now I am doing both.

If anyone has thoughts on this I would love to hear.

Also, I am trying to figure out the best way to incorporate @upsert, @index, @reverse, etc.

Two alternative APIs:

@index('name')
@reverse('borders')
class Region(Node):
    area: int
    population: int
    name: str
    borders: List[Region]



class Region(Node):
    area: int
    population: int
    name: str = Edge('index')
    borders: List[Region] = Edge('reverse')

Any thoughts or other syntax ideas are welcome.

Okay … so I went down a few different paths to see what would be the best (read: most intuitive and easy to use) syntax for adding directives (both with and without arguments).

I think what I came up with below demonstrates pretty well the flexibility:

class Region(Node):
    area: int
    population: int = index
    description: str = lang
    short_description: str = lang()
    name: str = index(fulltext)
    abbr: str = (index(exact), count, upsert)
    coord: geo
    borders: List[Region] = reverse

Notice here that I have taken the directives (reverse, index, etc) and made them importable. You can either just use them as is:

    description: str = lang

Or, instantiate them:

    short_description: str = lang()

Sometimes, you need to instantiate to be able to pass an argument:

    name: str = index(types.fulltext)

Other times, we can infer the argument. For example on an int index.

    population: int = index

And sometimes, you may want multiple directives. Just add them as a tuple:

    abbr: str = (index(types.exact), count, upsert)

When done … this should output the following schema:

Region: bool @index(bool) .
_type: string .
abbr: string @index(exact) @count @upsert .
area: int @index(int) .
borders: uid @reverse .
coord: geo .
description: string @lang .
name: string @index(fulltext) .
population: int @index(int) .
short_description: string @lang .
uid: int .

So, this is direction that I am heading. I plan to clean up the code with some more tests, add some transactional support and releasing a beta package on PyPI in the next week.

Thoughts and comments welcome.

1 Like

So I sort of discontinued working on this because I got dejected because of the licensing debate.

With the recent brave decision by @mrjn, I’m excited to get back to work on this project to bring dgraph and a fun API to the python community.

2 Likes

Glad to know @ahopkins ! :heart: