Giter VIP home page Giter VIP logo

kino_ecto's Introduction

build

KinoEcto - Ecto utilities for Livebook

For a better experience, see this Readme using Livebook.

KinoEcto is a collection of utilities for working with Ecto and Livebook to help developers learn more about Ecto but also as a way to augment project documentation for better onboarding, knowledge sharing, expectation setting, etc.

KinoEcto started as a hackathon project called Lively for Spawnfest 2022.

Mix.install(
  [
    {:ecto, "~> 3.8"},
    {:kino_ecto, git: "https://github.com/vorce/kino_ecto"}
  ],
  force: true
)

Existing components

  • KinoEcto.EntityRelationship: Visualize your Ecto.Schema as an ER diagram
  • KinoEcto.Explain: Visualize the query plan for an Ecto.Query.
  • KinoEcto.ChangesetValidator: Visualize an Ecto.Changeset by passing a changeset function and its attributes. The next step here would be having a SmartCell rendered for changing attributes and a better looking result of changeset evaluation.
  • KinoEcto.QueryBuilder: Build and return an Ecto.Query from the SQL query passed in the params.

Note: All the code is WIP and should be seen as such

Entity Relationship Diagram

Using the %KinoEcto.EntityRelantionship{schema: Module} you will be able to visualize a Entity Relationship Diagram and connected entities based on your Ecto.Schema.

Example:

defmodule Organization do
  use Ecto.Schema

  schema "organizations" do
    field(:name, :string)
    has_one(:team, Team)
  end
end

defmodule Team do
  use Ecto.Schema

  schema "teams" do
    field(:name, :string)
    has_many(:persons, Person)
  end
end

defmodule Person do
  use Ecto.Schema

  schema "persons" do
    field(:name, :string)
    field(:age, :integer)

    has_one(:team, Team)
    belongs_to(:organization, Organization)
  end
end

%KinoEcto.EntityRelationship{schema: Person}

Explain

To run explain on a query and output the graph in one go you can use KinoEcto.Explain.call(MyApp.Repo, :all, my_ecto_query).

If you already have a plan you can use it to draw a graph directly with: KinoEcto.Explain.Postgres.new(plan).

Note: Only Postgres is supported at the moment (myxql support issue)

Example

plan = [
  %{
    "Plan" => %{
      "Actual Loops" => 1,
      "Actual Rows" => 4,
      "Actual Startup Time" => 0.139,
      "Actual Total Time" => 0.231,
      "Node Type" => "Result",
      "Plan Rows" => 7,
      "Plan Width" => 405,
      "Plans" => [
        %{
          "Actual Loops" => 1,
          "Actual Rows" => 4,
          "Actual Startup Time" => 0.131,
          "Actual Total Time" => 0.218,
          "Node Type" => "Append",
          "Parent Relationship" => "Outer",
          "Plan Rows" => 7,
          "Plan Width" => 405,
          "Plans" => [
            %{
              "Actual Loops" => 1,
              "Actual Rows" => 0,
              "Actual Startup Time" => 0.009,
              "Actual Total Time" => 0.009,
              "Alias" => "paris",
              "Filter" => "(ar_num = 8)",
              "Index Cond" => "(tags ? 'tourism'::text)",
              "Index Name" => "idx_paris_tags",
              "Node Type" => "Index Scan",
              "Parent Relationship" => "Member",
              "Plan Rows" => 1,
              "Plan Width" => 450,
              "Relation Name" => "paris",
              "Scan Direction" => "NoMovement",
              "Startup Cost" => 0.0,
              "Total Cost" => 8.27
            },
            %{
              "Actual Loops" => 1,
              "Actual Rows" => 0,
              "Actual Startup Time" => 0.001,
              "Actual Total Time" => 0.001,
              "Alias" => "paris",
              "Filter" => "((tags ? 'tourism'::text) AND (ar_num = 8))",
              "Node Type" => "Seq Scan",
              "Parent Relationship" => "Member",
              "Plan Rows" => 1,
              "Plan Width" => 450,
              "Relation Name" => "paris_linestrings",
              "Startup Cost" => 0.0,
              "Total Cost" => 11.8
            },
            %{
              "Actual Loops" => 1,
              "Actual Rows" => 0,
              "Actual Startup Time" => 0.003,
              "Actual Total Time" => 0.003,
              "Alias" => "paris",
              "Filter" => "(ar_num = 8)",
              "Index Cond" => "(tags ? 'tourism'::text)",
              "Index Name" => "idx_paris_points_tags",
              "Node Type" => "Index Scan",
              "Parent Relationship" => "Member",
              "Plan Rows" => 1,
              "Plan Width" => 450,
              "Relation Name" => "paris_points",
              "Scan Direction" => "NoMovement",
              "Startup Cost" => 0.0,
              "Total Cost" => 8.27
            },
            %{
              "Actual Loops" => 1,
              "Actual Rows" => 0,
              "Actual Startup Time" => 0.002,
              "Actual Total Time" => 0.002,
              "Alias" => "paris",
              "Filter" => "(ar_num = 8)",
              "Index Cond" => "(tags ? 'tourism'::text)",
              "Index Name" => "idx_paris_polygons_tags",
              "Node Type" => "Index Scan",
              "Parent Relationship" => "Member",
              "Plan Rows" => 1,
              "Plan Width" => 450,
              "Relation Name" => "paris_polygons",
              "Scan Direction" => "NoMovement",
              "Startup Cost" => 0.0,
              "Total Cost" => 8.27
            },
            %{
              "Actual Loops" => 1,
              "Actual Rows" => 0,
              "Actual Startup Time" => 0.103,
              "Actual Total Time" => 0.103,
              "Alias" => "paris",
              "Filter" => "((tags ? 'tourism'::text) AND (ar_num = 8))",
              "Node Type" => "Seq Scan",
              "Parent Relationship" => "Member",
              "Plan Rows" => 1,
              "Plan Width" => 513,
              "Relation Name" => "paris_linestrings_ar_08",
              "Startup Cost" => 0.0,
              "Total Cost" => 7.27
            },
            %{
              "Actual Loops" => 1,
              "Actual Rows" => 4,
              "Actual Startup Time" => 0.009,
              "Actual Total Time" => 0.085,
              "Alias" => "paris",
              "Filter" => "((tags ? 'tourism'::text) AND (ar_num = 8))",
              "Node Type" => "Seq Scan",
              "Parent Relationship" => "Member",
              "Plan Rows" => 1,
              "Plan Width" => 72,
              "Relation Name" => "paris_points_ar_08",
              "Startup Cost" => 0.0,
              "Total Cost" => 5.16
            },
            %{
              "Actual Loops" => 1,
              "Actual Rows" => 0,
              "Actual Startup Time" => 0.007,
              "Actual Total Time" => 0.007,
              "Alias" => "paris",
              "Filter" => "((tags ? 'tourism'::text) AND (ar_num = 8))",
              "Node Type" => "Seq Scan",
              "Parent Relationship" => "Member",
              "Plan Rows" => 1,
              "Plan Width" => 450,
              "Relation Name" => "paris_polygons_ar_08",
              "Startup Cost" => 0.0,
              "Total Cost" => 1.08
            }
          ],
          "Startup Cost" => 0.0,
          "Total Cost" => 50.11
        }
      ],
      "Startup Cost" => 0.0,
      "Total Cost" => 50.13
    },
    "Execution Time" => 1.238,
    "Planning Time" => 0.92,
    "Triggers" => []
  }
]

KinoEcto.Explain.Postgres.new(plan)

Changeset Validator

A simpler way to visualize errors of applying changes to an entity using the changeset results. To use it you just need to create a struct %ChangesetValidator{fun: Module.changeset_func/arity, attrs: attrs}

Example

defmodule User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field(:name, :string)
    field(:age, :integer)
    field(:address, :string)
  end

  def new_user_changeset(name, age) do
    params = %{name: name, age: age}

    %__MODULE__{}
    |> cast(params, [:name, :age])
    |> validate_required([:name])
    |> validate_inclusion(:age, 18..100)
  end

  def update_address(user, address) do
    user
    |> cast(%{address: address}, [:address])
    |> validate_address()
  end

  defp validate_address(changeset) do
    Ecto.Changeset.add_error(changeset, :address, "Bad address", validation: :address_validation)
  end
end
%KinoEcto.ChangesetValidator{fun: &User.new_user_changeset/2, attrs: ["John Doe", 13]}
%KinoEcto.ChangesetValidator{
  fun: &User.update_address/2,
  attrs: [%User{name: "Jane Doe"}, "Street"]
}
%KinoEcto.ChangesetValidator{fun: &User.new_user_changeset/2, attrs: ["John Doe", 18]}

Generate Ecto Query from SQL

If a developer is more accustomed to SQL and not Ecto.Query, this tool can guide them on the construction of an Ecto.Query that replicated the SQL they expected.

Example

%KinoEcto.QueryBuilder{sql_query: "SELECT * FROM users WHERE name = 'John'"}

Future Work

Other than a lot of bug fixing, in the future, we want to improve the code we have so we can build a more solid and faster toolset that can enable users to have more information about their systems.

For now, we're checking what we can do with Ecto since it's a common library but we already thought about future work such as:

  • JSON visualizer
  • Oban tools
  • Ecto migration tracker (what and when was a given schema changed)

This can be just the beginning of KinoEcto.

kino_ecto's People

Contributors

vorce avatar thiagoramos23 avatar vittoriabitton avatar thetamind avatar dbernheisel avatar filipecabaco avatar joelremote avatar kianmeng avatar

Stargazers

Gonçalo Mendes Cabrita avatar Andrés Alejos avatar Mark Paolo Libunao avatar yujonglee avatar Chris Gregori avatar  avatar Ajay avatar Jace avatar Tiago Moraes avatar Andrei Riaskóv avatar Adam Eury avatar kyle avatar Raúl Santos avatar Bill Boz avatar  avatar  avatar Carl Gleisner avatar Wayan jimmy avatar  avatar  avatar  avatar Brooke avatar Akosua avatar Simon Ricard avatar Mayel de Borniol avatar Kasun Vithanage avatar Nelson Estevão avatar Jeremy Brayton avatar Carlo Gilmar avatar Philip Sampaio avatar Nikita avatar Henricus Louwhoff avatar Phillipp Ohlandt avatar Michael Crumm avatar Aayush Sahu avatar Steffen Deusch avatar Sebastian Henao avatar ivan avatar Jinkyou Son avatar  avatar Mike avatar Andrew Sullivan avatar peter madsen-mygdal avatar Simon Thépot avatar Brian Bonsignore  avatar Leandro Pereira avatar Greg Rychlewski avatar  avatar Franklin Rakotomalala avatar  avatar  avatar

Watchers

Leandro Pereira avatar Maarten van Vliet avatar  avatar  avatar  avatar  avatar  avatar  avatar

kino_ecto's Issues

Support for MyXQL for Explain

First: Not sure how useful the explain is from mysql dbs, and how easy it would be to visualize. Need to start there I think.

Second:

  • I attempted to add format=json actually giving back the data encoded from myxql here: elixir-ecto/myxql#159. But it wasn't really doable in a good way.
  • Next step might be to at least allow format json in ecto_sql (Ecto.Adapters.MyXQL.Connection.build_explain_query), and we can get a string of the json back and encode it ourselves.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.