Skip to content

Latest commit

 

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ExOdata4

An OData v4 query parser and Ecto query builder for Elixir. Parse OData query strings or full URIs and get back Ecto queries ready to execute against your own repo. Designed for exposing internal Elixir services to BI tools like Power BI and Excel.

Installation

Add ex_odata4 to your dependencies in mix.exs:

def deps do
  [
    {:ex_odata4, "~> 0.2.2"}
  ]
end

Configuration

Register your Ecto schemas in config/config.exs:

config :ex_odata4, schemas: %{
  "Orders" => MyApp.Orders,
  "Products" => MyApp.Products
}

OData field names are automatically derived from your schema — Ecto's snake_case atoms become PascalCase OData names. So :first_name is exposed as FirstName, :amount as Amount, and so on. No additional mapping is required.

Usage

Parsing a full OData URI

ExOdata4.parse_uri("/Orders?$filter=Amount gt 1000&$top=25&$skip=0")
|> MyApp.Repo.all()

Parsing a query string directly

ExOdata4.get("Orders", "$filter=Status eq 'active'&$orderby=Amount desc&$top=10")
|> MyApp.Repo.all()

Both functions return an %Ecto.Query{} ready to pipe into your repo.

$metadata

For Power BI and Excel, serve the $metadata document from your router:

# In your Phoenix router or Plug
get "/$metadata", fn conn, _ ->
  xml = ExOdata4.Metadata.generate(MyApp.Orders, namespace: "MyApp")
  conn
  |> put_resp_content_type("application/xml")
  |> send_resp(200, xml)
end

generate/2 accepts an optional :namespace (defaults to "Default").

Supported OData query options

Option Example
$filter $filter=Name eq 'John'
$top $top=25
$skip $skip=50
$orderby $orderby=Amount desc,Name asc
$metadata GET /$metadata

Filter operators

Operator Meaning
eq Equal
ne Not equal
gt Greater than
ge Greater than or equal
lt Less than
le Less than or equal
and Logical and
or Logical or

Filter functions

Function Example SQL
contains contains(Name, 'Jo') LIKE '%Jo%'
startswith startswith(Name, 'Jo') LIKE 'Jo%'
endswith endswith(Email, '.com') LIKE '%.com'
tolower tolower(Name) eq 'john' lower(name) = 'john'
toupper toupper(Name) eq 'JOHN' upper(name) = 'JOHN'
year year(Date) eq 2024 extract(year from date) = 2024
month month(Date) eq 1 extract(month from date) = 1
day day(Date) eq 15 extract(day from date) = 15
hour hour(Timestamp) gt 8 extract(hour from timestamp) > 8

Functions can be combined with logical operators:

$filter=contains(Name, 'John') and Amount gt 100
$filter=tolower(Status) eq 'active' or year(Date) gt 2023

Supported literal types

  • Strings: 'hello'
  • Integers: 42, -7
  • Decimals: 3.14
  • Booleans: true, false
  • Null: null
  • Dates: 2024-01-01
  • DateTimeOffset: 2024-01-01T00:00:00Z
  • GUIDs: 00000000-0000-0000-0000-000000000000

EDM type mapping

$metadata automatically maps Ecto types to OData EDM types:

Ecto type EDM type
:string Edm.String
:integer Edm.Int32
:float Edm.Double
:decimal Edm.Decimal
:boolean Edm.Boolean
:date Edm.Date
:utc_datetime / :naive_datetime Edm.DateTimeOffset
:binary_id Edm.Guid

Not yet supported

The goal is near-parity with the OData v4 spec. Features are shipped in order of practical value rather than spec completeness.

Feature Notes
$select Field projection
$count Total result count
not Logical negation
Math functions round(), floor(), ceiling()
$expand Loading related entities — planned, takes time to get right
Navigation properties e.g. Orders/Customer/Name eq 'John' — planned
Lambda operators any(), all()

Contributions are welcome. Open an issue or PR on GitHub.

License

MIT

About

Elixir OData v4 Hex Package

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages