{"id":15992182,"url":"https://github.com/aanastasiou/neoads","last_synced_at":"2026-06-17T21:02:03.481Z","repository":{"id":65904890,"uuid":"436672818","full_name":"aanastasiou/neoads","owner":"aanastasiou","description":"Abstract Data Structures over neo4j","archived":false,"fork":false,"pushed_at":"2025-01-08T16:54:16.000Z","size":413,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-03T16:50:33.619Z","etag":null,"topics":["data-modeling","data-science","data-structures","data-visualization","graph-theory","ogm"],"latest_commit_sha":null,"homepage":"https://neoads.readthedocs.io/en/latest/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aanastasiou.png","metadata":{"files":{"readme":"README.rst","changelog":"Changelog","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-09T15:48:27.000Z","updated_at":"2023-02-17T10:27:01.000Z","dependencies_parsed_at":"2023-02-23T03:30:17.423Z","dependency_job_id":"5a69256e-261c-46f8-af16-54c3846d7aed","html_url":"https://github.com/aanastasiou/neoads","commit_stats":{"total_commits":77,"total_committers":1,"mean_commits":77.0,"dds":0.0,"last_synced_commit":"7fba24cd570673b5bb51e20baed929663eda05d4"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aanastasiou%2Fneoads","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aanastasiou%2Fneoads/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aanastasiou%2Fneoads/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aanastasiou%2Fneoads/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aanastasiou","download_url":"https://codeload.github.com/aanastasiou/neoads/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247262981,"owners_count":20910333,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-modeling","data-science","data-structures","data-visualization","graph-theory","ogm"],"created_at":"2024-10-08T06:05:53.353Z","updated_at":"2026-06-17T21:01:58.435Z","avatar_url":"https://github.com/aanastasiou.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Abstract Data Structures over Neo4J\n===================================\n\nThe ``neoads`` module implements **interoperable** AbstractSet, AbstractMap and AbstractDLList \n(Doubly Linked List), over a Neo4J backend.\n\n.. figure:: neoads_banner.png\n   :align: center\n   :width: 1080\n\n   The ``AbstractDLList`` in action.\n\n\nIt relies on Neomodel for the OGM functionality and is designed to minimise\nround-trips to the database. In other words, if an operation can be carried out\nat Server Side, ``neoads`` will execute it as a query rather than attempt to\ninstantiate the data structure in local memory, modify it and then push it\nback to the backend.\n\n``neoads`` data structures are implemented in a way that is:\n\n* Agnostic to domain-specific data model\n    * You can create lists, sets or maps of *anything* in your data model, \n      simply by making ``ElementDomain`` the root object of your data model.\n\n* Completely transparent to the database backend\n    * The abstraction layer does not enforce a special organisation or\n      shortcuts and it is still possible to access the data structures in your \n      CYPHER queries.\n\nThe main benefit of ``neoads`` is that it makes it possible to store query results in\ndata structures within the DBMS and feed those data structures to further processing \n**without** re-running the queries themselves.\n\nIn addition to classes that model the Set, Map and Doubly Linked Lists, ``neoads``\nalso introduces a number of other entities such as ``CompositeString`` or ``SimpleDate``\nthat aim to blur the line between variables held in local RAM and variables held at the \ndatabase management system (Neo4j).\n\nThe ultimate goal is to be able to make ``neoads`` data structures completely transparent\nfrom the point of view of a high productivity language such as Python.\n\nThis means that ``neoads`` data structures could behave as drop-in replacements for data\nprocessing algorithms and interact \"naturally\" with other objects via operators and functions.\n\nFrom this point of view, Neo4J becomes a giant\n`heap \u003chttps://en.wikipedia.org/wiki/Memory_management#Dynamic_memory_allocation\u003e`_ available \nto higher level algorithms.\n\n\nInstallation\n------------\nAt the moment, it is possible to add ``neoads`` to your ``virtualenv`` directly from\nthis repository via::\n\n    \u003e pip install git+https://...\n\n``neoads`` relies on `neomodel \u003chttps://github.com/neo4j-contrib/neomodel\u003e`_ and assumes\nthat:\n\n1. A Neo4j instance with proper access rights is already available via a known\n   Bolt URL\n\n2. Neomodel's ``db.set_connection()`` has already been called prior to any further calls\n   to ``neoads``. (Or more generally, ``neomodel`` has already been initialised).\n\n\nQuickstart\n----------\nThis section contains the bear minimum usage examples for a user to get up and\nrunning with ``neoads``. \n\nFor more information, please refer to the \n`detailed documentation in ReadTheDocs \u003chttps://neoads.readthedocs.io/en/latest/index.html\u003e`_, \nor in ``doc/``.\n\nWorking with Simple Variables (Number, Date)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nCreate a simple variable called ``answer``::\n\n    u = SimpleNumber(42, \"answer\").save()\n\n``u`` is now a data object that provides full access to the ``SimpleNumber``. To\nrecall it from the database management system simply use: ::\n\n    v = SimpleNumber.nodes.get(name=\"answer\")\n\nThe exact same example applies for ``SimpleDate`` with the exception that the value\nargument must be a standard Python ``datetime`` object.\n\nWorking with Composite Variables (Strings, Arrays of Strings, Dates, Numbers)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nCreate a string variable called ``greeting``\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n::\n\n    u = CompositeString(\"Hello World\", \"greeting\").save()\n\nIn addition to all other operations that can be applied to ``u``, it is also possible\nto access its contents via::\n\n    print(u[2])\n\nCreate an Array of strings variable called ``greetings``\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n::\n\n    u = CompositeArrayString([\"Hello\", \"Hola\", \"Χαίρετε\"], \"greetings\").save()\n\nA similar initialisation pattern applies to `CompositeArrayNumber, CompositeArrayDate`.\n\n\nWorking with Abstract Data Structures (Set, Map, Doubly Linked List)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTo work with abstract data structures we need to set up a few things first.\nHere are a few strings whose use will become apparent shortly: ::\n\n    colours_a = [CompositeString(\"RED\", \"A_RED\").save(),\n                 CompositeString(\"GREEN\", \"A_GREEN\").save(),\n                 CompositeString(\"BLUE\", \"A_BLUE\").save()]\n\n    colours_b = [CompositeString(\"RED\", \"B_RED\").save(),\n                 CompositeString(\"GREEN\", \"B_GREEN\").save(),\n                 CompositeString(\"PURPLE\", \"B_PURPLE\").save()]\n\nCreate two sets::\n\n    colours_a_set = AbstractSet(name=\"A_COLOURS\").save()\n    colours_b_set = AbstractSet(name=\"B_COLOURS\").save()\n\nAdd the items::\n\n    for a_colour in colours_a:\n        colours_a_set.add(a_colour)\n\n    for b_colour in colours_b:\n        colours_b_set.add(b_colour)\n\nNow, sets can be combined with operators as in::\n\n    union_of_colour_sets = colours_a_set | colours_b_set\n\nOr in more complex ways, such as this way of evaluating the symmetric difference::\n\n    symm_diff_colour = (colours_a_set - colours_b_set) | (colours_b_set-colours_a_set)\n\n\nIt is worth noting that all operators used above **do** produce intermediate objects with\nthe result of partial evaluations (for example, there are two intermediate sets that are produced\nwith the partial results needed to evaluate the `__or__`). These can be cleared via\ngarbage collection.\n\nAll of these operations have taken place at **server side**. None of the sets had\nto travel to the client side, be processed and then be pushed out to the server\nagain.\n\nSimilarly, it is possible to create `AbstractMap`, `AbstractDLList` objects. For more information\nplease refer `to the documentation \u003chttps://neoads.readthedocs.io/en/latest/index.html\u003e`_.\n\nAbstract data structures over arbitrary data model entities\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\n``neoads`` is completely agnostic to the sort of entities its abstract data structures point to,\n*so long as* these descend from a common ancestor of type ``ElementDomain``.\n\nThe following is a minimal example of how to setup lists of persons living in specific geographical\nregions. Notice here the way an arbitrary data model gets integrated with ``neoads``. ::\n\n    class PersonalRelationship(neomodel.StructredRel):\n        \"\"\"\n        A very simple assocation class between entities of type Person that bears the date the\n        acquaintance was made.\n        \"\"\"\n        on_date = neomodel.DateTimeProperty(default_now=True)\n\n    class Country(neoads.ElementDomain):\n        uid = neomodel.UniqueIdProperty()\n        name = neomodel.StringProperty()\n\n    class Person(neoads.ElementDomain):\n        uid = neomodel.UniqueIdProperty()\n        full_name = neomodel.StringProperty()\n        acquainted_with = neomodel.RelationshipTo(\"Person\", \"ACQUAINTED_WITH\", model = PersonalRelationship)\n        lives_in = neomodel.RelationshipTo(\"Country\", \"LIVES_IN\")\n\n\nNow, given this data model, we can instantiate a double linked list at server side **just** by running\na simple query::\n\n    # First of all create the list\n    some_abstract_list = neoads.AbstractDLList(name=\"EU_27_PERSONS\").save()\n    # The populate it\n    some_abstract_list.from_query(\"MATCH (ListItem:Person)-[LIVES_IN]-\u003e(b:Country) \"\n                                  \"WHERE b.name IN ['Austria', 'Belgium', 'Bulgaria', 'Croatia', 'Cyprus', 'Czechia', \"\n                                  \"'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', \"\n                                  \"'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', \"\n                                  \"'Poland', 'Portugal', 'Romania', 'Slovakia', 'Slovenia', 'Spain', 'Sweden'] \")\n\nAt this point, ``some_abstract_list`` will be populated with all ``Person`` that have a connection to\na ``Country`` within the European Union's EU27 countries. This of course was *by name*.\n\nWith ``neoads`` it would also have been possible to first create lists of ``Country`` that belong within\na specific geographical region (e.g. Europe, Asia, Americas, etc) and then create a list (still from\na query) that makes use of a previously defined ``neoads`` list.\n\nIn addition to this and since ``neoads`` is completely agnostic to the types of elements its abstract\ndata structures can hold, it is possible to have abstract data structures point to other abstract\ndata structures creating higher complexity structures if required.\n\nFor example a ``neoads`` abstract list of lists can be accessed via ``some_list[0][1][\"Alpha\"][2]``. The\nfirst indexing returns ``neoads.AbstractDLList`` whose indexing operation returns ``neoads.AbstractDLList``\nthat in turn returns a ``neoads.AbstractMap`` whose value might be another ``neoads.AbstractDLList``\nfrom which we return the element at index ``2``.\n\n``neoads`` data structures remain completely re-usable at server side.\n\nFor more information please see the documentation on abstract data structures.\n\n\nWhat happens to unnamed entities? (garbage collection)\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\nUnnamed entities are saved at the DBMS but unless their ``name`` attribute has been saved (or\nthey have been renamed) it is impossible for them to be retrieved once a reference to them\nhas been lost.\n\nThis is of course by design, to cover for cases where an intermediate data structure\nis required, but is not required to be saved.\n\nFor those particular cases, ``neoads`` provides a very simple \"garbage collector\".\nThe garbage collector is basically a set of scripts that look for specific variables\nand erase them.\n\nThese variables are as follows:\n\n1. Variables whose name conforms to a UUID4 identifier and do not have any connection\n   with any other entity in the system\n\n2. Data structure entities that are not connected to any data structure\n\nAnd this brings us nicely to the Memory Manager.\n\nMemory Management\n^^^^^^^^^^^^^^^^^\nThe objective of the memory manager is to group together a number of operations that\nmight be required for the maintenance of the DBMS state.\n\nAt the moment, the only operations that are available via the memory manager are listing objects,\ngetting a reference to an object and performing garbage collection.\n\nOther operations that are planned are:\n\n1. Lost+Found / recovery operations\n2. Optimisation\n3. Backup and Restoring\n\nMinimal `MemoryManager` example:\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\n`MemoryManager` objects are straightforward to initialise and work with. The following\nexample assumes that the ``NEO4J_BOLT_URL`` environment variable has already been set. ::\n\n    import random\n    import neoads\n\n    mm = neoads.MemoryManager()\n    some_elements = [neoads.SimpleNumber(random.random()).save() for k in range(0,10)]\n    # Let's perform a dir() on the DBMS for the variables we just set\n    objects_in_mem = mm.list_objects()\n    # list_objects() returns a dictionary where the key is the name of a variable and value is\n    # the object of the variable itself.\n    # Let's get a reference to an object\n    some_object = mm.get_object(objects_in_mem[0])\n    # At this point some_object is of type `SimpleNumber` (or whatever appropriate type) and\n    # we can apply any further operations on it.\n\nDocumentation\n-------------\nModule documentation is available in ``doc/`` as a standard sphinx\ndocumentation project or over at `ReadTheDocs \u003chttps://neoads.readthedocs.io/en/latest/index.html\u003e`_\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faanastasiou%2Fneoads","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faanastasiou%2Fneoads","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faanastasiou%2Fneoads/lists"}