{"id":13508882,"url":"https://github.com/gameanalytics/panoramix","last_synced_at":"2025-04-05T06:09:42.405Z","repository":{"id":34943298,"uuid":"141406896","full_name":"GameAnalytics/panoramix","owner":"GameAnalytics","description":"Apache Druid client for Elixir","archived":false,"fork":false,"pushed_at":"2025-03-11T10:16:20.000Z","size":263,"stargazers_count":43,"open_issues_count":1,"forks_count":7,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-05T06:09:34.750Z","etag":null,"topics":["analytics","druid","druid-io","elixir","erlang","olap","real-time","timeseries"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/GameAnalytics.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-18T08:44:28.000Z","updated_at":"2025-03-11T10:16:17.000Z","dependencies_parsed_at":"2024-02-15T17:32:39.151Z","dependency_job_id":"64bbb239-6dc4-4fdf-9ce3-ecb8c3bb9122","html_url":"https://github.com/GameAnalytics/panoramix","commit_stats":{"total_commits":162,"total_committers":11,"mean_commits":"14.727272727272727","dds":0.5679012345679013,"last_synced_commit":"78ba443890caec18e4bfb18ff5a42711b4c882c8"},"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GameAnalytics%2Fpanoramix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GameAnalytics%2Fpanoramix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GameAnalytics%2Fpanoramix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GameAnalytics%2Fpanoramix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GameAnalytics","download_url":"https://codeload.github.com/GameAnalytics/panoramix/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247294541,"owners_count":20915340,"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":["analytics","druid","druid-io","elixir","erlang","olap","real-time","timeseries"],"created_at":"2024-08-01T02:00:59.904Z","updated_at":"2025-04-05T06:09:42.389Z","avatar_url":"https://github.com/GameAnalytics.png","language":"Elixir","funding_links":[],"categories":["ORM and Datamapping"],"sub_categories":[],"readme":"# Panoramix\n\n[![Build Status](https://travis-ci.org/GameAnalytics/panoramix.svg?branch=master)](https://travis-ci.org/GameAnalytics/panoramix)\n\nAn open-source client library for sending requests to [Apache Druid][druid] from applications written in Elixir. The project uses [HTTPoison][httpoison] as an HTTP client for sending queries.\n\n[druid]: http://druid.io/\n[httpoison]: https://github.com/edgurgel/httpoison\n\n## Getting Started\n\nAdd Panoramix as a dependency to your project.\n\n```elixir\ndefp deps do\n  [\n    {:panoramix, \"\u003e= 0.12.0 and \u003c 1.0.0\"}\n  ]\nend\n```\n\n## Configuration \n\nPanoramix requires a Druid Broker profile to be defined in the configuration of your application.\n\n```elixir\nconfig :panoramix,\n  request_timeout: 120_000,\n  query_priority:  0,\n  broker_profiles: [\n    default: [\n      base_url:       \"https://druid-broker-host:9088\",\n      cacertfile:     \"path/to/druid-certificate.crt\",\n      http_username:  \"username\",\n      http_password:  \"password\"\n    ]\n  ],\n  httpoison_module: HTTPoison\n```\n\n* `request_timeout`: Query timeout in millis to be used in [`Context`](context-druid-doc-link) of all Druid queries. \n* `query_priority`: Priority to be used in [`Context`](context-druid-doc-link) of all Druid queries. \n* `httpoison_module`: Module to call when making HTTP requests. Defaults to `HTTPoison` if not specified, but you can provide a custom wrapper module if you wish. See [HTTPoison.Base](https://hexdocs.pm/httpoison/HTTPoison.Base.html) for examples.\n\n[context-druid-doc-link]: http://druid.io/docs/latest/querying/query-context.html\n\nThe `cacertfile` option in the broker profile names a file that contains the CA certificate for the Druid broker. Alternatively you can specify the certificate as a string in PEM format (starting with `-----BEGIN CERTIFICATE-----`) in the `cacert` option.\n\n## Usage\n\nBuild a query like this:\n\n```elixir\nuse Panoramix\n\nq = from \"my_datasource\",\n      query_type: \"timeseries\",\n      intervals: [\"2019-03-01T00:00:00+00:00/2019-03-04T00:00:00+00:00\"],\n      granularity: :day,\n      filter: dimensions.foo == \"bar\",\n       aggregations: [event_count: count(), \n                      unique_id_count: hyperUnique(:user_unique)]  \n```\n\nAnd then send it:\n\n```elixir\nPanoramix.post_query(q, :default)\n```\n\nWhere `:default` is a configuration profile pointing to your Druid server.\n\nThe default value for the profile argument is `:default`, so if you\nonly need a single configuration you can omit it:\n\n```elixir\nPanoramix.post_query(q)\n```\n\nResponse example:\n```elixir\n{:ok,\n [\n   %{\n     \"result\" =\u003e %{\n       \"event_count\" =\u003e 7544,\n       \"unique_id_count\" =\u003e 43.18210933535\n     },\n     \"timestamp\" =\u003e \"2019-03-01T00:00:00.000Z\"\n   },\n   %{\n     \"result\" =\u003e %{\n       \"event_count\" =\u003e 1051,\n       \"unique_id_count\" =\u003e 104.02052398847\n     },\n     \"timestamp\" =\u003e \"2019-03-02T00:00:00.000Z\"\n   },\n   %{\n     \"result\" =\u003e %{\n       \"event_count\" =\u003e 4591,\n       \"unique_id_count\" =\u003e 79.19885795313\n     },\n     \"timestamp\" =\u003e \"2019-03-03T00:00:00.000Z\"\n   }\n ]}\n```\n\nTo make a nested query, pass a map of the form `%{type: :query, query: inner_query}`\nas data source. For example:\n\n```elixir\nuse Panoramix\n\ninner_query = from \"my_datasource\",\n                query_type: \"topN\",\n                intervals: [\"2019-03-01T00:00:00+00:00/2019-03-04T00:00:00+00:00\"],\n                granularity: :day,\n                aggregations: [event_count: count()],\n                dimension: \"foo\",\n                metric: \"event_count\",\n                threshold: 100\nq = from %{type: :query, query: inner_query},\n      query_type: \"timeseries\",\n      intervals: [\"2019-03-01T00:00:00+00:00/2019-03-04T00:00:00+00:00\"],\n      granularity: :day,\n      aggregations: [foo_count: count(),\n                     event_count_sum: longSum(:event_count)],\n      post_aggregations: [mean_events_per_foo: aggregations.event_count_sum / aggregations.foo_count]\n```\n\nTo make a join query, pass a map of the form `%{type: :join, left: left, right: right,\njoinType: :INNER | :LEFT, rightPrefix: \"prefix_\", condition: \"condition\"}`. Both the left\nand the right side can be a nested query as above, `%{type: :query, query: inner_query}`,\nwhich will be expanded. Other join sources will be passed to Druid unchanged. For example:\n\n```elixir\nuse Panoramix\n\nfrom %{type: :join,\n       left: \"sales\",\n       right: %{type: :lookup, lookup: \"store_to_country\"},\n       rightPrefix: \"r.\",\n       condition: \"store == \\\"r.k\\\"\",\n       joinType: :INNER},\n  query_type: \"groupBy\",\n  intervals: [\"0000/3000\"],\n  granularity: \"all\",\n  dimensions: [%{type: \"default\", outputName: \"country\", dimension: \"r.v\"}],\n  aggregations: [country_revenue: longSum(:revenue)]\n```\n\nYou can also build a JSON query yourself by passing it as a map to\n`post_query`:\n\n```elixir\nPanoramix.post_query(%{queryType: \"timeBoundary\", dataSource: \"my_datasource\"})\n```\n\n## Troubleshooting\n\nYou can check correctness of your configuration by requesting status from Druid Broker. A successfull response will look like this.\n\n```elixir\niex(1)\u003e Panoramix.status(:default)\n{:ok,\n %{\n   \"memory\" =\u003e %{...},\n   \"modules\" =\u003e [...],\n   \"version\" =\u003e \"0.13.0\"\n }}\n```\n\n## Contributions\nWe'd love to accept your contributions in a form of patches, bug reports and new features! \n\nBefore opening a pull request please make sure your changes pass all the tests. \n\n## License\nExcept as otherwise noted this software is licensed under the [Apache License, Version 2.0]((http://www.apache.org/licenses/LICENSE-2.0))\n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the \nLicense. You may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \n\"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the \nspecific language governing permissions and limitations under the License.\n\nThe code was Copyright 2018-2019 Game Analytics Limited and/or its affiliates. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgameanalytics%2Fpanoramix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgameanalytics%2Fpanoramix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgameanalytics%2Fpanoramix/lists"}