{"id":13879411,"url":"https://github.com/bitquery/activecube","last_synced_at":"2025-07-16T15:32:13.260Z","repository":{"id":40759909,"uuid":"240958874","full_name":"bitquery/activecube","owner":"bitquery","description":"Multi-Dimensional Queries with Rails","archived":false,"fork":false,"pushed_at":"2024-08-20T07:02:14.000Z","size":160,"stargazers_count":24,"open_issues_count":4,"forks_count":2,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-11-18T04:04:11.203Z","etag":null,"topics":["analytics","cube","gem","olap","rails"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","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/bitquery.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2020-02-16T20:14:14.000Z","updated_at":"2024-09-23T00:16:08.000Z","dependencies_parsed_at":"2023-02-12T05:30:56.399Z","dependency_job_id":"74cbd724-2815-4ad6-a810-3e25a05d5b85","html_url":"https://github.com/bitquery/activecube","commit_stats":{"total_commits":135,"total_committers":3,"mean_commits":45.0,"dds":"0.18518518518518523","last_synced_commit":"de2e1bbeea8af8ac686d9d6d58c32dcdbe544a8b"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitquery%2Factivecube","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitquery%2Factivecube/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitquery%2Factivecube/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitquery%2Factivecube/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bitquery","download_url":"https://codeload.github.com/bitquery/activecube/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226143895,"owners_count":17580245,"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","cube","gem","olap","rails"],"created_at":"2024-08-06T08:02:20.227Z","updated_at":"2024-11-24T08:31:19.356Z","avatar_url":"https://github.com/bitquery.png","language":"Ruby","readme":"# Activecube: Multi-Dimensional Queries with Rails\n\nActivecube is the library to make multi-dimensional queries to data warehouse, such as:\n\n```ruby\nCube.slice(\n    date: cube.dimensions[:date][:date].format('%Y-%m'),\n    currency: cube.dimensions[:currency][:symbol]\n).measure(:count).\nwhen(cube.selectors[:currency].in('USD','EUR')).to_sql\n```\n\nCube, dimensions, metrics and selectors are defined in the Model, similary to\nActiveRecord.\n\nActivecube uses Rails ActiveRecord in implementation. \n\nIn particular, you have to define all tables, used in\nActivecube, as ActiveRecord tables.\n\n \n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'activecube'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install activecube\n\n## Usage\n\nBasic steps to use ActiveCube are:\n\n1. Define your database schema in models, as you do with Rails\n2. Setup connection properties to data warehouse in config/database.yml. You can use multiple connections \nif you use Rails 6 or higher\n3. Define cubes in models, sub-classed from Activecube::Base. Look \n[spec/models/test/transfers_cube.rb](spec/models/test/transfers_cube.rb) as example\n4. Make queries to the cubes\n\nCheck [spec/cases/activecube_spec.rb](spec/cases/activecube_spec.rb) for more examples.\n\n\n### Cube definition \n\nCube defined using the following attributes:\n\n- **table** specifies, which physical database tables can be considered to query\n```ruby\n    table TransfersCurrency\n    table TransfersFrom\n    table TransfersTo\n```\n\n- **dimension** specifies classes used for slicing the cube\n\n```ruby\n    dimension date: Dimension::Date,\n              currency: Dimension::Currency\n```\n\nOr \n\n```ruby\n    dimension date: Dimension::Date\n    dimension currency: Dimension::Currency\n```\n\n- **metric** specifies which results expected from the cube queries\n\n```ruby\n    metric amount: Metric::Amount,\n           count: Metric::Count\n```\n\nOr\n\n```ruby\n    metric amount: Metric::Amount\n    metric count: Metric::Count\n```\n\n- **selector** is a set of expressions, which can filter results\n\n```ruby\n    selector currency: CurrencySelector,\n             transfer_from: TransferFromSelector,\n             transfer_to: TransferToSelector\n```\n\n### Table definition\n\nTables are defined as regular active records, with additional optional attribute 'index':\n```ruby\nindex 'currency_id', cardinality: 4\n```\n\nwhich means that the table has an index onm currency_id field, with average number of different entries\nof 10,000 ( 10^4). This creates a hint for optimizer to build queries.\n\nIndexes can span multiple fields, as\n\n```ruby\nindex ['currency_id','date'], cardinality: 6\n```\n\nNote, that if you created combined index in database, you most probable will need to define all\nindexed combinations, for example:\n\n```ruby\nindex ['currency_id'], cardinality: 4\nindex ['currency_id','date'], cardinality: 6\n```\n\nYou can require using index in some cases. If required: true added, the table will be used **only** in case when this field is used\nin query metric, dimension or selector.\n```ruby\nindex ['currency_id'], cardinality: 4, required: true\n```\n\n### Query language\n\nYou use the cube class to create and execute queries.\n\nQueries can be expressed as Arel query, SQL or executed against the database, returning results.\n\nThe methods used to contruct the query:\n\n- **slice** defines which dimensions slices the results\n- **measure** defines what to measure\n- **when** defines which selectors to apply\n- **desc, asc, offset, limit** are for ordering and limiting result set\n\nAfter the query contructed, the following methods can be applied:\n\n- **to_sql** to generate String SQL query from cube query\n- **to_query** to generate Arel query\n- **query** to execute query and return ResultSet\n\n### Managing Connections\n\n\nYou can control the connection used to construct and execute query by \nActiveRecord standard API:\n\n```ruby\nApplicationRecord.connected_to(database: :data_warehouse) do\n      cube = My::TransfersCube\n      cube.slice(\n              date: cube.dimensions[:date][:date].format('%Y-%m'),\n              currency: cube.dimensions[:currency][:symbol]\n      ).measure(:count).query\n    end\n```\n\nwill query using data_warehouse configuraton.\n\n\nAlternatively you can use the method provided by activecube. It will \nmake the connection for the model or abstract class, which is super class for your models:\n\n```ruby\nMy::TransfersCube.connected_to(database: :data_warehouse) do |cube|\n      cube.slice(\n              date: cube.dimensions[:date][:date].format('%Y-%m'),\n              currency: cube.dimensions[:currency][:symbol]\n      ).measure(:count).query\n    end\n```\n\n## How it works\n\nWhen you construct and execute cube query with any outcome ( sql, Arel query or ResultSet),\nthe same sequence of operations happen:\n\n1) Cube is collecting the query into a set of objects from the chain method call;\n2) Query is matched against the physical tables, the tables are selected that can serve the query or its part. For example, one table can provide one set of metrics, and the other can provide remaining;\n3) If possible, the variant is selected from all possible options, which uses indexes with the most cardinality\n4) Query is constructed using Arel SQL engine ( included in ActiveRecord ) using selected tables, and possibly joins\n5) If requested, the query is converted to sql ( using Arel visitor ) or executed with database connection\n\n## Optimization\n\nThe optimization on step #3 try to minimize the total cost of execution:\n\n![Formula min max](https://latex.codecogs.com/png.latex?min(\\sum_{tables}max_{metrics}(cost))))\n\nwhere \n\n![Formula cost](https://latex.codecogs.com/png.latex?\\inline\u0026space;cost(metric,table)\u0026space;=\u0026space;1\u0026space;/\u0026space;(1\u0026space;\u0026plus;\u0026space;cardinality(metric,\u0026space;table)))\n\nOptimization is done using the algorithm, which checks possible combinations of metrics and tables.\n\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## RSPec tests\n\n\nTo run tests, you need clickhouse server installation.\nTests use database 'test' that have to be created in clickhouse as:\n```sql\nCREATE DATABASE test;\n```\nCheck credentials for connection in [spec/spec_helper.rb](spec/spec_helper.rb) file.\nBy default clickhouse must reside on \"clickhouse\" server name, port 8123 with the default user access open.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/bitquery/activecube. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the Activecube project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/bitquery/activecube/blob/master/CODE_OF_CONDUCT.md).\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitquery%2Factivecube","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitquery%2Factivecube","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitquery%2Factivecube/lists"}