Skip to content
/ invana Public

Python API for modelling and managing graphs.

Notifications You must be signed in to change notification settings

invana/invana

Repository files navigation

invana

Python API for Apache TinkerPop's Gremlin supported databases.

Apache license Commit Activity codecov

Features

  • Object Mapper - Models, PropertyTypes, and Form validation
  • Execute gremlin queries
  • Built in QuerySets for performing standard CRUD operations on graph.
  • Utilities for logging queries and performance.
  • Django-ORM like search when using OGM(ex: has__id__within=[200752, 82032, 4320], has__name__startingWith="Per")( Refer search-usage.md for more)
  • Index support
  • Query caching support
  • Asynchronous Python API.

Installation

pip install git+https://github.com/invanalabs/invana-py.git#egg=invana

or 
# for latest code
pipenv install git+https://github.com/invana/invana@dev#egg=invana

Usage

Model first graph

from invana import InvanaGraph
from invana.ogm.models import VertexModel, EdgeModel
from invana.ogm.fields import StringProperty, DateTimeProperty, IntegerProperty, FloatProperty, BooleanProperty
from datetime import datetime
from invana.ogm import indexes

graph = InvanaGraph("ws://megamind.local:8182/gremlin", traversal_source="g")


class Project(VertexModel):
    graph = graph
    properties = {
        'name': StringProperty(max_length=10, trim_whitespaces=True),
        'description': StringProperty(allow_null=True, min_length=10),
        'rating': FloatProperty(allow_null=True),
        'is_active': BooleanProperty(default=True),
        'created_at': DateTimeProperty(default=lambda: datetime.now())
    }
    indexes = (
        indexes.CompositeIndex("name"),
        indexes.CompositeIndex("created_at")
    )


class Person(VertexModel):
    graph = graph
    properties = {
        'first_name': StringProperty(min_length=5, trim_whitespaces=True),
        'last_name': StringProperty(allow_null=True),
        'username': StringProperty(default="rrmerugu"),
        'member_since': IntegerProperty(),

    }
    indexes = (
        indexes.CompositeIndex("username"),
    )


class Authored(EdgeModel):
    graph = graph
    properties = {
        'created_at': DateTimeProperty(default=lambda: datetime.now())
    }
    indexes = (
        indexes.CompositeIndex("created_at")
    )


graph.management.create_model(Project)
graph.management.rollback_open_transactions(i_understand=True)
graph.management.create_indexes_from_model(Project)

Project.objects.delete()
Person.objects.delete()
Authored.objects.delete()

person = Person.objects.get_or_create(first_name="Ravi Raja", last_name="Merugu", member_since=2000)
project = Project.objects.create(name="Hello   ", rating=2.5, is_active=False)
authored_data = Authored.objects.create(person.id, project.id)

authored_list = Authored.objects.search().to_list()
project_list = Project.objects.search().to_list()

graph.close_connection()

Searching graph

# search by id
Project.objects.search(has__id=123).to_list()
Project.objects.search(has__id__within=[123, 232]).to_list()

# search string - eq, neq, startingWith, endingWith, containing, notStartingWith, notEndingWith, notContaining
Project.objects.search(has__name__eq="Ravi Raja").to_list()
Project.objects.search(has__name__neq="Ravi Raja").to_list()
Project.objects.search(has__name__neq="Ravi Raja").to_list()
Project.objects.search(has__name__startingWith="Ravi").to_list()
Project.objects.search(has__name__endingWith="Raja").to_list()
Project.objects.search(has__name__containing="Raja").to_list()
Project.objects.search(has__name__notStartingWith="Raja").to_list()
Project.objects.search(has__name__notEndingWith="Raja").to_list()
Project.objects.search(has__name__notContaining="Raja").to_list()

# lt, gt, lte, gte, inside, outside, between
Project.objects.search(has__member_since__lte=3000).to_list()
Project.objects.search(has__member_since__lt=3000).to_list()
Project.objects.search(has__member_since__gte=1999).to_list()
Project.objects.search(has__member_since__gt=1999).to_list()
Project.objects.search(has__member_since__inside=(1000, 3000)).to_list()
Project.objects.search(has__member_since__outside=(1000, 3000)).to_list()
Project.objects.search(has__member_since__between=(1000, 3000)).to_list()

Note: more info on usage here

Order by

You need to add indexing to run order queries efficiently. Read more here

Project.objects.search().order_by('name').to_list()  # asc order
Project.objects.search().order_by('-name').to_list()  # desc order

Pagination

# using range for pagination
queryset = Project.objects.search().order_by('name').range(1, 10).to_list()

# using paginator
from invana.gremlin.paginator import QuerySetPaginator

page_size = 5
queryset = Project.objects.search().order_by("-serial_no")
paginator = QuerySetPaginator(queryset, page_size)
qs = paginator.page(1)
first_page = qs.to_list()

Running queries

using execute_query method

from invana import InvanaGraph

graph = InvanaGraph("ws://localhost:8182/gremlin", username="user", password="password")
results = graph.execute_query("g.V().limit(1).toList()", timeout=180)
graph.close_connection()

using execute_query_with_callback method

from invana import InvanaGraph

graph = InvanaGraph("ws://localhost:8182/gremlin", username="user", password="password")
graph.execute_query_with_callback("g.V().limit(1).next()",
                                  lambda res: print(res.__len__()),
                                  finished_callback=lambda: graph.close_connection(),
                                  timeout=180)
graph.close_connection()

Setup test

docker run --restart=unless-stopped -p 8182:8182 -d --name janusgraph janusgraph/janusgraph:latest
docker run --restart=unless-stopped -p 8184:8182 -d --name janusgraph-1 janusgraph/janusgraph:latest

Supported graph databases

License

Apache License, version 2.0