What does HackerNews think of ecto?
A toolkit for data mapping and language integrated query.
Is there a significant difference?
Phoenix comes with its own database tool called Ecto[0] which is excellent, and it uses Postgres by default. If you're not intended to leverage CouchDB for offline support you should go Postgres without a second thought.
That said, I'm also curious about how to implement offline support with Phoenix in a nice and trivial way.
On that topic, I really enjoy working in Elixir because Ecto [1] lets you write "SQL" with Elixir's composable functional syntax. It sits somewhere between "the language is compiled to SQL" and ORM. The Ruby-esque syntax took some getting used to, but once I was past that hurdle my productivity skyrocketed. It's not 100% feature complete compatibility with all the different SQL dialects, but most of what you'll need is there.
DB development usually uses Ecto: https://github.com/elixir-ecto/ecto, it has OTP servers for connection pooling and other tasks.
For managing background tasks I use Honeydew: https://github.com/koudelka/honeydew
"Ecto is a toolkit for data mapping and language integrated query for Elixir."
Ryan Bigg wrote a great book about breaking away from AR in Rails called Exploding Rails[3]. It's a good read.
[0] http://sequel.jeremyevans.net/
[1] https://github.com/elixir-ecto/ecto
[2] https://rom-rb.org/5.0/learn/introduction/active-record/
Plain-old-data DB record types (and their respective owner modules) are to ORMs, as code-gen'ed wire record types (and their respective owner modules) are to ProtoBuf-like wire-format codec libraries. They're the containers you put data into and take it out of, to ensure it's in the right types when being sent to, or received from, your DB. No more than that.
And, corollary to this idea: the POD record-type MyDB.FooTable shouldn't be taken as a type to use to stuff in the results from any random query you run against the DB table `foo`. It should be taken to represent the specific row type that the DB table `foo` has—the one you get when you `SELECT * from foo`. If you do an SQL query, and the result of that SQL query has fewer or extra or differently-typed columns, then the resulting row-set will have a different record type, and so you should have a separate POD data-type that does match these records. (Example: if you join two tables with a query, you don't need the ORM to spit out two records objects with a fancy OO-style relationship between them; you just need one record type that represents the resulting joined rows, without any particular idea that it happens to be an amalgamation of data from two tables.)
For an example of this approach to ORM, see Elixir's https://github.com/elixir-ecto/ecto.
It surprises me that the author used Elixir for implementation but didn't just write a query DSL utilizing the Elixir macro facilities [0]. A macro based DSL likely would be much simpler than writing a new grammar and builds on existing language tooling. Ecto for example does a great job (IMHO) of making a sane DSL for SQL [1]. Given data processing primitives provided by Elixir libraries like Streams and GenStage [2] it seems it'd be pretty straightforward to implement.
Take for example the "segmented query path expression". It is handled fine with a minor modification as an Elixir quoted statement:
`quote do: @query_path//foo/goo[instance='a2']/blah/x[a='S 512']/y`
That yields a well defined AST tree base on the Elixir syntax: ``` {:@, [context: Elixir, import: Kernel], [ {:path, [context: Elixir], [ {:/, [context: Elixir, import: Kernel], [ ... ```
The closures work as well by switching the dollar signs to ampersands: `quote do: select(@path/foo/&x/&goo/z/y/w, { value(&1) < 3 })`
Makes me want to write an Ecto adapter for dealing with Elixir Streams. =)
0: https://hackernoon.com/understanding-elixir-macros-3464e1414... 1: https://github.com/elixir-ecto/ecto 2: https://hexdocs.pm/gen_stage/GenStage.html
# Query all rows in the "users" table, filtering for users whose age is > 18, and selecting their name
"users"
|> where([u], u.age > 18)
|> select([u], u.name)
# Build a dynamic query fragment based on some parameters
dynamic = false
dynamic =
if params["is_public"] do
dynamic([p], p.is_public or ^dynamic)
else
dynamic
end
dynamic =
if params["allow_reviewers"] do
dynamic([p, a], a.reviewer == true or ^dynamic)
else
dynamic
end
from "posts", where: ^dynamic
Across all the different means of interacting with a database I have experience with (from full-fledged ORMs like ActiveRecord, to sprocs in ASP.NET), I've found that it offers the best compromise between providing an ergonomic abstraction over the database, and not hiding all of the nitty-gritty details you need to worry about in order to write performant queries or use database-specific features like triggers or window functions.My main point, though, is that you don't need to reach for NoSQL if all you need is a way to compose queries without string interpolation.
In the functional world, there obviously aren't objects and structs are not the same things as objects in the OOP sense.
Even then the key part of a ORM is the "R". ORMs allow you to utilize a RDBMS transparently as if you were manipulating the OOP objects.
A couple of projects that this is closer to than Django ORM.
https://github.com/elixir-ecto/ecto -> "Database Wrapper" http://www.yesodweb.com/book/persistent -> "data store interface" https://hackage.haskell.org/package/groundhog -> "Database Mapping"
The wonderful thing about it is that you don't have to get all type annotations since the begining, you can add them over the time. This allows to use "unspecified" types for quick prototyping, and make them more specific when the project evolves, which is a major complain of traditional type systems such as Java's.
Another tool that I find useful is Credo[1] which "is a static code analysis tool for the Elixir language with a focus on teaching and code consistency".
The Ecto[2] project uses a tool called Ebert[3] that automatically runs Credo for each pull-request and comments with the issues found. Here you can see an example of Ebert's bot commenting on a PR[4]
[1] https://github.com/rrrene/credo [2] https://github.com/elixir-ecto/ecto [3] https://ebertapp.io/ [4] https://github.com/elixir-ecto/ecto/pull/1785