{"id":14973962,"url":"https://github.com/inaka/sumo_db","last_synced_at":"2025-04-05T02:09:11.265Z","repository":{"id":9550750,"uuid":"11457919","full_name":"inaka/sumo_db","owner":"inaka","description":"Erlang Persistency Framework","archived":false,"fork":false,"pushed_at":"2022-11-10T09:08:43.000Z","size":1216,"stargazers_count":175,"open_issues_count":20,"forks_count":38,"subscribers_count":62,"default_branch":"main","last_synced_at":"2025-03-29T01:09:16.843Z","etag":null,"topics":["database-adapter","database-management","elasticsearch","erlang","mongodb","mysql","pgsql","riak","sumo-db"],"latest_commit_sha":null,"homepage":"http://inaka.github.io/sumo_db/","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inaka.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-07-16T19:15:37.000Z","updated_at":"2025-03-26T05:42:43.000Z","dependencies_parsed_at":"2023-01-13T15:25:41.199Z","dependency_job_id":null,"html_url":"https://github.com/inaka/sumo_db","commit_stats":null,"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fsumo_db","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fsumo_db/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fsumo_db/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fsumo_db/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inaka","download_url":"https://codeload.github.com/inaka/sumo_db/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247276164,"owners_count":20912288,"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":["database-adapter","database-management","elasticsearch","erlang","mongodb","mysql","pgsql","riak","sumo-db"],"created_at":"2024-09-24T13:49:44.873Z","updated_at":"2025-04-05T02:09:11.238Z","avatar_url":"https://github.com/inaka.png","language":"Erlang","readme":"[![Stories in Ready](https://badge.waffle.io/inaka/sumo_db.png?label=ready\u0026title=Ready)](https://waffle.io/inaka/sumo_db)\n# sumo_db\n\n[![Build Status](https://travis-ci.org/inaka/sumo_db.svg?branch=master)](https://travis-ci.org/inaka/sumo_db)\n\n## About\n\nThis is a work in progress. There's also [an article][sumo-article] about\nsumo_db. This articles might be a little outdated by now, but can still\nprovide some basic information on how to get started.\n\n**sumo_db** aims to ease db access for erlang applications. It offers a very\nsimple persistance layer capable of interacting with different db's, while\noffering a consistent api to your code.\n\n\n## Contact Us\nIf you find any **bugs** or have a **problem** while using this library, please\n[open an issue][issue] in this repo (or a pull request :)).\n\n## Overview\n\n * sumo_db gives you a standard way to define your db schema, regardless of the\n db implementation (mongo, mysql, redis, elasticsearch, etc.).\n\n * Your entities encapsulate behavior in code (i.e. functions in a module) and\n state in a `sumo:doc()` implementation.\n\n * `sumo` is the main module. It translates to and from sumo internal records\n into your own state.\n\n * Each store is managed by a worker pool of processes, each one using a module\n that implements `sumo_store` and calls the actual db driver\n (e.g: `sumo_store_mnesia`).\n\n * Some native domain events are supported, that are dispatched through a\n `gen_event:notify/2` automatically when an entity is created, updated, deleted.\n Also when a schema is created and when all entities of a given type are\n deleted. Events are described in [this article][domain-article].\n\n * Full conditional logic support when using `find_by/2` and `delete_by/2`\n function. You can find more information about the syntax of this conditional\n logic operators [here][cond-syntax].\n\n * Support for sorting (`asc` or `desc`) based on multiple fields unsing\n `find_by/5` and `find_all/4` functions. For example this\n `[{age, desc}, {name, asc}]]` will sort descendently by `age` and ascendently\n  by `name`.\n\n * Support for docs/models validations through `sumo_changeset` (check out the\n   [Changeset](#changeset) section).\n\n\n## Backends, Stores and Repositories modules\n\nThese three concepts have a specific meaning in the context of sumo_db.\n\n - **Backend**: establishes and holds a single Database instance connection.\n\n - **Store**: implements the specific operations that modify the contents of the\n backend and retrieves the information it holds.\n\n - **Repository**: the application that uses `sumo_db` should implement one\n repository for each entity that's defined in it. The repository is the module\n that bridges the model and the store.\n\n\n## Supported Backends/Adapters\n\n - [Mnesia](http://erlang.org/doc/man/mnesia.html) – **built-in with `sumo`**\n - [Riak](https://github.com/inaka/sumo_db_riak)\n - [PostgreSQL](https://github.com/inaka/sumo_db_pgsql)\n - [MySQL](https://github.com/inaka/sumo_db_mysql)\n - [MongoDB](https://github.com/inaka/sumo_db_mongo)\n - [ElasticSearch](https://github.com/inaka/sumo_db_elasticsearch)\n\n\n## Implementing an Adapter\n\nTo implement an adapter, you must implement two specific behaviours:\n\n - [**sumo_backend**](./src/sumo_backend.erl) – DB connection\n - [**sumo_store**](./src/sumo_store.erl) – implements the **Sumo API**\n\n \u003e You can check the implemented adapters mentioned above in order to have\n   a better idea about how to build a custom adapter from scratch.\n\n\n## Events\n\nSumo dispatches events when things happen. An Event has this structure:\n```erlang\n{EventId, Model, Event, Args}\n```\n- `EventId` is a `reference()` which identifies the event\n- `Model` is the model where the event happend, for example, if we are creating a new entitiy in the model `people` the value of `Model` would be `people`.\n- `Event` is the type of the event.\n- `Args` extra data sent.\n\nSupported types of events:\n\n- `pre_persisted` just before persisting some entity. This event has the entity we want to persist as `Args`. It is dispatched on this function:\n    - `sumo:persist/2`\n- `persisted` just after persisting some entity. This event has the persisted entity as `Args`. This Event has the same `EventId` as its `pre_persisted` event. It is dispatched on this function:\n    - `sumo:persist/2`\n- `pre_delete_all` just before deleting all entities for a model. This event has no `Args`. It is dispatched on this function:\n    - `sumo:delete_all/1`\n- `deleted_all` just after deleting all entities for a model. This event has no `Args`. This Event has the same `EventId` as its `pre_delete_all` event. It is dispatched on this function:\n    - `sumo:delete_all/1`\n- `pre_deleted` just before deleting an entity. This event has the entity id as `Args`. It is dispatched on this function:\n    - `sumo:delete/2`\n- `deleted` just after deleting an entity. This event has the entity id as `Args`. This Event has the same `EventId` as its `pre_deleted` event. It is dispatched on this function:\n    - `sumo:delete/2`\n- `pre_deleted_total` just before deleting by some delete conditions. This event has the sumo conditions as `Args`. It is dispatched on this function:\n    - `sumo:delete_by/2`\n- `deleted_total` just after deleting by some delete conditions. This event has a list with the number of entities deleted and the delete conditions as `Args`. This Event has the same `EventId` as its `pre_deleted_total` event. It is dispatched on this function:\n    - `sumo:delete_by/2`\n- `pre_schema_created` just before creating a sumo schema. This event has no `Args`. It is dispatched on this function:\n    - `sumo:create_schema/2`\n- `schema_created` just after creating a sumo schema. This event has no `Args`. This Event has the same `EventId` as its `pre_schema_created` event. It is dispatched on this function:\n    - `sumo:create_schema/2`\n\nSumo requires users to add their own `gen_event`'s in order to handle those events. In order to add them Users have to configure sumo properly. In the `config` file we can add them like this under `sumo_db` configuration:\n\n```erlang\n{events, [\n   {'_', sumo_test_people_events_manager},\n   {people, sumo_test_people_events_manager}\n ]}\n```\n\nSumo allows us to add a `gen_event` to one type of model (i.e. `people`) or for all (`'_'`).\n\n\n## Changeset\n\nThis feature is inspired by Elixir [Ecto.Changeset](https://hexdocs.pm/ecto/Ecto.Changeset.html),\nand the module that implements this feature is [sumo_changeset](./src/utils/sumo_changeset.erl).\n\n### Changeset Usage Example\n\n \u003e **NOTE:** This example uses [**FancyFlow**](https://github.com/ferd/fancyflow)\n   in order to pipe changeset functions in a nicer way.\n\n```erlang\n%% suppose you have a model/doc `person`, and that module provides a function\n%% to encapsulate model/doc creation\nPerson = person:new(\u003c\u003c\"John\"\u003e\u003e, \u003c\u003c\"Doe\"\u003e\u003e),\n\n%% create the set of params/changes\nParams = #{age =\u003e 33, id =\u003e 1, \u003c\u003c\"last_name\"\u003e\u003e =\u003e \u003c\u003c\"other\"\u003e\u003e},\n\n%% run the changeset\nChangeset = [pipe](people,\n  sumo_changeset:cast(_, Person, Params, [id, first_name, last_name, age, status]),\n  sumo_changeset:validate_required(_, [id, last_name, status]),\n  sumo_changeset:validate_inclusion(_, status, [\u003c\u003c\"active\"\u003e\u003e, \u003c\u003c\"blocked\"\u003e\u003e]),\n  sumo_changeset:validate_number(_, age, [{less_than_or_equal_to, 18}]),\n  sumo_changeset:validate_length(_, last_name, [{min, 3}]),\n  sumo_changeset:validate_format(_, last_name, \u003c\u003c\"^[a-zA-Z]\"\u003e\u003e)),\n```\n\n\n## Examples\n\nSee: [**examples/blog**][example-blog] for a full example. To run it, while\nbeing in the top level directory:\n\n    make all blog\n\nSee: [**examples/custom_store**](examples/custom_store) for creating your own Store. To run it, follow the instructions in this README\n\n\n## Running Dialyzer\n\n```\n$ rebar3 dialyzer\n```\n\n\n## Running Tests\n\n```\n$ rebar3 ct\n```\n\n## Change Log\n\nAll notable changes to this project will be documented in the\n[CHANGELOG.md](CHANGELOG.md).\n\n\n## Contributors\n\nWe want to thank all of [our contributors](CONTRIBUTORS.md) for their hard work\n:muscle:.\n\n [sumo-article]: http://marcelog.github.com/articles/erlang_persistence_entities.html\n [domain-article]: http://marcelog.github.com/articles/erlang_epers_persist_entities_domain_events.html\n [issue]: https://github.com/inaka/sumo_db/issues/new\n [example-blog]: https://github.com/inaka/sumo_db/tree/master/examples/blog\n [cond-syntax]: https://github.com/inaka/sumo_db/wiki/Conditional-Logic-Syntax\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finaka%2Fsumo_db","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finaka%2Fsumo_db","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finaka%2Fsumo_db/lists"}