{"id":23175765,"url":"https://github.com/selecto-elixir/selecto","last_synced_at":"2025-08-18T10:31:40.909Z","repository":{"id":62727150,"uuid":"549335967","full_name":"selecto-elixir/selecto","owner":"selecto-elixir","description":"A query builder for Ecto","archived":false,"fork":false,"pushed_at":"2023-03-29T04:04:12.000Z","size":203,"stargazers_count":3,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-17T17:50:03.460Z","etag":null,"topics":["ecto","elixir"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/selecto-elixir.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-10-11T03:06:13.000Z","updated_at":"2023-09-02T02:29:44.000Z","dependencies_parsed_at":"2023-02-08T20:31:45.345Z","dependency_job_id":null,"html_url":"https://github.com/selecto-elixir/selecto","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/selecto-elixir%2Fselecto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/selecto-elixir%2Fselecto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/selecto-elixir%2Fselecto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/selecto-elixir%2Fselecto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/selecto-elixir","download_url":"https://codeload.github.com/selecto-elixir/selecto/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230164195,"owners_count":18183351,"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":["ecto","elixir"],"created_at":"2024-12-18T06:09:21.957Z","updated_at":"2024-12-18T06:09:22.422Z","avatar_url":"https://github.com/selecto-elixir.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Selecto\n\nA Query Writing System\n\nSelecto allows you to create queries within a configured domain. The domain has a main table that will\nalways be included in queries, required filters that will always be applied, and a tree of available\ntables for joins.\n\nThis is very young software and might spill milk in your computer. \n\nThe documentation here is probably out of date but better documentation is planned once the API is finalized.\n\nFor now, see [selecto_test](https://github.com/selecto-elixir/selecto_test).\n\nSelecto is configured by passing in a 'domain' which tells it which \ntable to start at, which tables it can join (assocs are supported now, \nbut I will be adding support for non-assoc \u0026 parameterized joins), \nwhat columns are available, and what filters are available \n\n```elixir\nselecto = Selecto.configure( YourApp.Repo,  domain )\n```\n\nThe domain is a map, and contains:\n\n- source: (req) This is the starting point, the table that will always be included in the query, as the module name, Eg YourApp.Accounts.Users\n- columns: A map of definitions and metadata for schema columns composed columns.\n- filters: A map of ad hoc filters.\n- joins: A keyword list containing assoc names to maps which can also recursively contain joins, columns, filters, and name (name is required currently). The joins need to be set up as proper associatinos in your schema!\n- required_filters: This is a list of filters that will always be applied to the query. This is where you'd put a filter telling Selecto to restrict results, such as if you have fk based multi-tenant or want to build queryies restricted to a certain context. A quirk of the way filters are converted means that a fitler is required, or the system will add 'false'\n\n```elixir\ndomain = %{\n  name: \"Solar System\",\n  source: SelectoTest.Test.SolarSystem,\n  joins: [ ### Joins require a Name currently-- may change and allow a format similar to the list from preload\n    planets: %{\n      name: \"Planet\",\n      joins: [\n        satellites: %{\n          name: \"Satellites\"\n        }\n      ],\n    }\n  ],\n  ### These filters will always be applied to a query in the domain. \n  required_filters: [{\"id\", [1, 2, 3, 4, 5, 6]}]\n}\n```\n\nSelecto will walk through the configuration and configure columns from the ecto schema. From the source table, \nthey will be named as a string of the column name. Columns in associated tables will be given name as association atom \nand the column name in brackets, eg \"assoctable[id]\".\n\nTo select data, use Selecto.select: \n\n```elixir\nSelecto.select(selecto, [\"id\", \"name\", \"assoctable[id]\" ])\n```\n\nTo filter data, use Selecto.filter: \n\n```elixir\nSelecto.filter(selecto, [ {\"id\", 1} ])\n```\n\nSelecto will add the joins needed to build the query for the requested selections. It currently uses left joins only, but I will add support to specify join type.\n\nTo get results, use Selecto.execute\n\n```elixir\nSelecto.execute(selecto)\n```\n\nSelections in Detail\n\nWhen func is referenced below, it is referring to a SQL function\n\n```elixir\n    \"field\" # - plain old field from one of the tables\n    {:field, field } #- same as above disamg for predicate second+ position FAILS\n    {:literal, \"value\"} #- for literal values\n    {:literal, 1.0}  ##FAILS\n    {:literal, 1}\n    {:literal, datetime} etc\n    {:func, SELECTOR}\n    {:count} (for count(*))\n    {:func, SELECTOR, SELECTOR}\n    {:func, SELECTOR, SELECTOR, SELECTOR} #...\n    {:extract, part, SELECTOR}\n    {:case, [PREDICATE, SELECTOR, ..., :else, SELECTOR]}\n\n    {:coalese, [SELECTOR, SELECTOR, ...]}\n    {:greatest, [SELECTOR, SELECTOR, ...]}\n    {:least, [SELECTOR, SELECTOR, ...]}\n    {:nullif, [SELECTOR, LITERAL_SELECTOR]} #LITERAL_SELECTOR means naked value treated as lit not field\n\n    {:subquery, ...}\n#TODO\n    {:operator, OP, SELECTOR}\n    {:operator, OP, SELECTOR, SELECTOR}\n\n```\n\nFilters in Detail\n\nA filter is given as a tuple with the following forms allowed:\n\n- {field, value} (value is string, numbe, boolean) -\u003e regular old =\n- {field, nil} -\u003e is null clause\n- {field, list_of_valeus } -\u003e in clause\n- {field, {comp, value}} -\u003e comp is !=, \u003e, \u003c, \u003e=, \u003c=\n- {field, {between, min, max}}-\u003e you get it\n- {field, :not_true} -\u003e gives not(field) (should be a bool...)\n- {:or, [list of filters]} -\u003e recurses, joining items in the list with OR puts the result in ()\n- {:and, [list of filters]} -\u003e recurses and puts the result in ()\n\nPlanned Features:\n\n- more special form function support\n- support for operators\n- ability to configure without requiring domain structure\n- support timescale, postgis\n- Many 'TODO' sprinkled around the code +\n- custom joins, columns, self join\n- parameterized joins (eg joining against a flags or tags table )\n- json/array selects and predicates\n- subqueries in filters\n- correlated subqueries\n- ability to tell selecto to put some selects into an array from a subquery\n- ability to select full schema structs / arrays of schema structs\n- tests (when domain/filters/select is stabilized)\n- Documentation\n- CTEs\n- Window functions\n- UNion, etc - pass in list of predicates and query will union all the alts together\n- index hints\n- join controls - eg manually add a join and tell Selecto which join variant\n- form integration for validation selections and providing options to form elements\n- more flexable selector and predicate structures, allow joins to use any predicates\n\nPlanned new format :\n\n```elixir\n    #standardize predicate format FUTURE NOT AVAILABLE YET! \n\n    {SELECTOR} # for boolean fields FAILS\n    {SELECTOR, nil} #is null\n    {SELECTOR, :not_nil} #is not null\n    {SELECTOR, SELECTOR} #= require literal FAILS\n    {SELECTOR, [SELECTOR2, ...]}# in ()\n    {SELECTOR, {comp, SELECTOR2}} #\u003c= etc\n    {SELECTOR, {:between, SELECTOR2, SELECTOR2}\n    {:not, PREDICATE}\n    {:and, [PREDICATES]}\n    {:or, [PREDICATES]}\n    {SELECTOR, :in, SUBQUERY}\n    {SELECTOR, comp, {:subquery, :any, SUBQUERY}}  ## Or :all \n    {:exists, SUBQUERY}\n\n \n```\n\n## Installation\n\nIf [available in Hex](https://hex.pm/docs/publish), the package can be installed\nby adding `selecto` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:selecto, \"~\u003e 0.2.6\"}\n  ]\nend\n```\n\n\n\n\n\nDocumentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)\nand published on [HexDocs](https://hexdocs.pm). Once published, the docs can\nbe found at \u003chttps://hexdocs.pm/selecto\u003e.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fselecto-elixir%2Fselecto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fselecto-elixir%2Fselecto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fselecto-elixir%2Fselecto/lists"}