Giter VIP home page Giter VIP logo

mabel-dev / opteryx Goto Github PK

View Code? Open in Web Editor NEW
45.0 5.0 10.0 319.63 MB

🦖 A SQL-on-everything Query Engine you can execute over multiple databases and file formats. Query your data, where it lives.

Home Page: https://opteryx.dev

License: Apache License 2.0

Python 96.94% Rust 0.34% Makefile 0.07% Cython 2.65%
python sql analytics big-data serverless sql-engine arrow cloud-run cloud-native query-engine

opteryx's Introduction

Opteryx

Query your data, where it lives.

A unified SQL interface to unlock insights across your diverse data sources, from blobs stores to databases - effortless cross-platform data analytics.

What is Opteryx?

Opteryx champions the SQL-on-everything approach, streamlining cross-platform data analytics by federating SQL queries across diverse data sources, including database systems like Postgres and datalake file formats like Parquet. The goal is to enhance your data analytics process by offering a unified way to access data from across your organization.

Opteryx is a Python library that combines elements of in-process database engines like SQLite and DuckDB with federative features found in systems like Presto and Trino. The result is a versatile tool for querying data across multiple data sources in a seamless fashion.

Opteryx offers the following features:

  • SQL queries on data files generated by other processes, such as logs
  • A command-line tool for filtering, transforming, and combining files
  • Integration with familiar tools like pandas and Polars
  • Embeddable as a low-cost engine, enabling portability and allowing for hundreds of analysts to leverage ad hoc databases with ease
  • Unified and federated access to data on disk, in the cloud, and in on-premises databases, not only through the same interface but in the same query

How Does it Work?

Opteryx processes queries by first determining the appropriate language to interact with various downstream data platforms. It translates your query into SQL, CQL, or a suitable query format for document stores like MongoDB, depending on the data source. This allows Opteryx to efficiently retrieve the necessary data from systems such as MySQL or MongoDB to respond to your query.

Opteryx

Why Use Opteryx?

Familiar Interface

Opteryx supports key parts of the Python DBAPI and SQL92 standard standards which many analysts and engineers will already know how to use.

Consistent Syntax

Opteryx creates a common SQL-layer over multiple data platforms, allowing backend systems to be upgraded, migrated or consolidated without changing any Opteryx code.

Where possible, errors and warnings returned by Opteryx help the user to understand how to fix their statement to reduce time-to-success for even novice SQL users.

Consumption-Based Billing Friendly

Opteryx is well-suited for deployments to environments which are pay-as-you-use, like Google Cloud Run. Great for situations where you have low-volume usage, or multiple environments, where the costs of many traditional database deployment can quickly add up.

Python Ecosystem

Opteryx is Open Source Python, it quickly and easily integrates into Python code, including Jupyter Notebooks, so you can start querying your data within a few minutes. Opteryx integrates with many of your favorite Python data tools, you can use Opteryx to run SQL against pandas and Polars DataFrames, and even execute a JOIN on an in-memory DataFrame and a remote SQL dataset.

Time Travel

Designed for data analytics in environments where decisions need to be replayable, Opteryx allows you to query data as at a point in time in the past to replay decision algorithms against facts as they were known in the past. You can even self-join tables historic data, great for finding deltas in datasets over time. (data must be structured to enable temporal queries)

Fast

Benchmarks on M2 Pro Mac running an ad hoc GROUP BY over a 6 million row parquet file via the CLI in ~1/4th of a second from a cold start (no caching and predefined schema). (different systems will have different performance characteristics)

Instant Elasticity

Designed to run in Knative and similar environments like Google Cloud Run, Opteryx can scale down to zero, and scale up to respond to thousands of concurrent queries within seconds.

Bring your own Data

Opteryx

Opteryx supports multiple query engines, dataframe APIs and storage formats. You can mix-and-match sources in a single query. Opteryx can even JOIN datasets stored in different formats and different platforms in the same query, such as Parquet and MySQL.

Opteryx allows you to query your data directly in the systems where they are stored, eliminating the need to duplicate data into a common store for analytics. This saves you the cost and effort of maintaining duplicates.

Opteryx can push parts of your query to the source query engine, allowing queries to run at the speed of the backend, rather than your local computer.

And if there's not a connector in the box for your data platform; bespoke connectors can be added.

Install

Installing from PyPI is recommended.

pip install opteryx

To build Opteryx from source, refer to the contribution guides.

Opteryx installs with a small set of libraries it needs for core functionality, such as Numpy, PyArrow, and orjson. Some features require additional libraries to be installed, you are notified of these libraries as they are required.

Examples

Filter a Dataset on the Command Line
Execute a Simple Query in Python
Execute SQL on a pandas DataFrame
Query Data on Local Disk
Query Data on GCS
Query Data in SQLite

Try Opteryx now using our interactive labs on Binder.

Binder

Filter a Dataset on the Command Line

In this example, we are running Opteryx from the command line to filter one of the internal example datasets and display the results on the console.

python -m opteryx "SELECT * FROM \$astronauts WHERE 'Apollo 11' IN UNNEST(missions);"

Opteryx this example is complete and should run as-is

Execute a Simple Query in Python

In this example, we are showing the basic usage of the Python API by executing a simple query that makes no references to any datasets.

# Import the Opteryx SQL query engine library.
import opteryx

# Execute a SQL query to evaluate the expression 4 * 7.
# The result is stored in the 'result' variable.
result = opteryx.query("SELECT 4 * 7;")

# Display the first row(s) of the result to verify the query executed correctly.
result.head()
ID 4 * 7
1 28

this example is complete and should run as-is

Execute SQL on a pandas DataFrame

In this example, we are running a SQL statement on a pandas DataFrame and returning the result as a new pandas DataFrame.

# Required imports
import opteryx
import pandas

# Read data from the exoplanets.csv file hosted on Google Cloud Storage
# The resulting DataFrame is stored in the variable `pandas_df`.
pandas_df = pandas.read_csv("https://storage.googleapis.com/opteryx/exoplanets/exoplanets.csv")

# Register the pandas DataFrame with Opteryx under the alias "exoplanets"
# This makes the DataFrame available for SQL-like queries.
opteryx.register_df("exoplanets", pandas_df)

# Perform an SQL query to group the data by `koi_disposition` and count the number
# of occurrences of each distinct `koi_disposition`.
# The result is stored in `aggregated_df`.
aggregated_df = opteryx.query("SELECT koi_disposition, COUNT(*) FROM exoplanets GROUP BY koi_disposition;").pandas()

# Display the aggregated DataFrame to get a preview of the result.
aggregated_df.head()
  koi_disposition  COUNT(*)
0       CONFIRMED      2293
1  FALSE POSITIVE      5023
2       CANDIDATE      2248 

this example is complete and should run as-is

Query Data on Local Disk

In this example, we are querying and filtering a file directly. This example will not run as written because the file being queried does not exist.

# Import the Opteryx query engine.
import opteryx

# Execute a SQL query to select the first 5 rows from the 'space_missions.parquet' table.
# The result will be stored in the 'result' variable.
result = opteryx.query("SELECT * FROM 'space_missions.parquet' LIMIT 5;")

# Display the result.
# This is useful for quick inspection of the data.
result.head()
ID Company Location Price Launched_at Rocket Rocket_Status Mission Mission_Status
0 RVSN USSR Site 1/5, Baikonur Cosmodrome, null 1957-10-04 19:28:00 Sputnik 8K71PS Retired Sputnik-1 Success
1 RVSN USSR Site 1/5, Baikonur Cosmodrome, null 1957-11-03 02:30:00 Sputnik 8K71PS Retired Sputnik-2 Success
2 US Navy LC-18A, Cape Canaveral AFS, Fl null 1957-12-06 16:44:00 Vanguard Retired Vanguard TV3 Failure
3 AMBA LC-26A, Cape Canaveral AFS, Fl null 1958-02-01 03:48:00 Juno I Retired Explorer 1 Success
4 US Navy LC-18A, Cape Canaveral AFS, Fl null 1958-02-05 07:33:00 Vanguard Retired Vanguard TV3BU Failure

this example requires a data file, space_missions.parquet.

Query Data in SQLite

In this example, we are querying a SQLite database via Opteryx. This example will not run as written because the file being queried does not exist.

# Import the Opteryx query engine and the SqlConnector from its connectors module.
import opteryx
from opteryx.connectors import SqlConnector

# Register a new data store with the prefix "sql", specifying the SQL Connector to handle it.
# This allows queries with the 'sql' prefix to be routed to the appropriate SQL database.
opteryx.register_store(
   prefix="sql",  # Prefix for distinguishing this particular store
   connector=SqlConnector,  # Specify the connector to handle queries for this store
   remove_prefix=True,  # Remove the prefix from the table name when querying SQLite
   connection="sqlite:///database.db"  # SQLAlchemy connection string for the SQLite database
)

# Execute a SQL query to select specified columns from the 'planets' table in the SQL store,
# limiting the output to 5 rows. The result is stored in the 'result' variable.
result = opteryx.query("SELECT name, mass, diameter, density FROM sql.planets LIMIT 5;")

# Display the result.
# This is useful for quickly verifying that the query executed correctly.
result.head()
ID name mass diameter density
1 Mercury 0.33 4879 5427
2 Venus 4.87 12104 5243
3 Earth 5.97 12756 5514
4 Mars 0.642 6792 3933
5 Jupiter 1898.0 142984 1326

this example requires a data file, database.db.

Query Data on GCS

In this example, we are to querying a dataset on GCS in a public bucket called 'opteryx'.

# Import the Opteryx query engine and the GcpCloudStorageConnector from its connectors module.
import opteryx
from opteryx.connectors import GcpCloudStorageConnector

# Register a new data store named 'opteryx', specifying the GcpCloudStorageConnector to handle it.
# This allows queries for this particular store to be routed to the appropriate GCP Cloud Storage bucket.
opteryx.register_store(
    "opteryx",  # Name of the store to register
    GcpCloudStorageConnector  # Connector to handle queries for this store
)

# Execute a SQL query to select all columns from the 'space_missions' table located in the 'opteryx' store,
# and limit the output to 5 rows. The result is stored in the 'result' variable.
result = opteryx.query("SELECT * FROM opteryx.space_missions LIMIT 5;")

# Display the result.
# This is useful for quickly verifying that the query executed correctly.
result.head()
ID Company Location Price Launched_at Rocket Rocket_Status Mission Mission_Status
0 RVSN USSR Site 1/5, Baikonur Cosmodrome, null 1957-10-04 19:28:00 Sputnik 8K71PS Retired Sputnik-1 Success
1 RVSN USSR Site 1/5, Baikonur Cosmodrome, null 1957-11-03 02:30:00 Sputnik 8K71PS Retired Sputnik-2 Success
2 US Navy LC-18A, Cape Canaveral AFS, Fl null 1957-12-06 16:44:00 Vanguard Retired Vanguard TV3 Failure
3 AMBA LC-26A, Cape Canaveral AFS, Fl null 1958-02-01 03:48:00 Juno I Retired Explorer 1 Success
4 US Navy LC-18A, Cape Canaveral AFS, Fl null 1958-02-05 07:33:00 Vanguard Retired Vanguard TV3BU Failure

this example is complete and should run as-is

Further Examples

For further examples, check out the interactive labs on Binder.

Community

Discord X Follow

Get Involved

  • Star this repo
  • Contribute — join us in building Opteryx, through writing code, or inspiring others to use it.
  • Let us know your ideas, how you are using Opteryx, or report a bug or feature request.
  • See the contributor documentation for Opteryx. It's easy to get started, and we're really friendly if you need any help!
  • If you're interested in contributing to the code now, check out GitHub issues. Feel free to ask questions or open a draft PR.

Security

Static Analysis Vulnerabilities Security Rating

See the project Security Policy for information about reporting vulnerabilities.

License

License FOSSA Status

Opteryx is licensed under Apache 2.0 except where specific modules note otherwise.

Status

Status

Opteryx is in beta. Beta means different things to different people, to us, being beta means:

  • Core functionality has good regression test coverage to help ensure stability
  • Some edge cases may have undetected bugs
  • Performance tuning is incomplete
  • Changes are focused on feature completion, bugs, performance, reducing debt, and security
  • Code structure and APIs are not stable and may change

Related Projects

  • orso DataFrame library
  • mabel Streaming data APIs
  • mesos MySQL connector for Opteryx

opteryx's People

Contributors

cclauss avatar dependabot[bot] avatar fossabot avatar gitter-badger avatar gva-jjoyce avatar gva-nigelclarke avatar joocer avatar xb500 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

opteryx's Issues

[BUG] Conversion to list of Dicts is too slow for practical use

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behaviour:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behaviour
A clear and concise description of what you expected to happen.

Screenshots or Logs
If applicable, add screenshots or logs to help explain your problem.
!! Be aware not to publish any sensitive information in what you upload.

Environment (please complete the following information):

  • OS: [e.g. iOS]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

✨ Support for CTEs (with statements)

WITH x AS (SELECT a, MAX(b) AS b FROM t GROUP BY a)
SELECT a, b FROM x;

Initial design

Execute first, then keep the result in memory. Include in the subsequent queries as a LITERAL_TABLE.

This would probably work with a new reader (literal_tabel_reader), which would also need to be added to the factory.

[FEATURE] table aliases

  • FROM table AS name

need to be able to SELECT, WHERE, ORDER and GROUP on the columns using either the aliases or the original names.

projection should result in unambiguous names, but not be just adding the prefixes to the column names

[BUG] GROUP BY should split by the column, if if it's not in the projection

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behaviour:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behaviour
A clear and concise description of what you expected to happen.

Screenshots or Logs
If applicable, add screenshots or logs to help explain your problem.
!! Be aware not to publish any sensitive information in what you upload.

Environment (please complete the following information):

  • OS: [e.g. iOS]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

[FEATURE] Temporal clauses should be localized

FOR clauses should be specific to the context, for example

SELECT event
FROM log
FOR TODAY
WHERE event NOT IN 
   (
    SELECT event
    FROM log
    FOR YESTERDAY
    )

This will need an alternate implementation to the RegEx based one.

[FEATURE] Use inbuilt AST features

NOT DONE =>

    /// SUBSTRING(<expr> [FROM <expr>] [FOR <expr>])
    Substring {
        expr: Box<Expr>,
        substring_from: Option<Box<Expr>>,
        substring_for: Option<Box<Expr>>,
    },
SELECT TRIM('#! ' FROM '    #SQL Tutorial!    ') AS TrimmedString;
    /// TRIM([BOTH | LEADING | TRAILING] <expr> [FROM <expr>])\
    /// Or\
    /// TRIM(<expr>)
    Trim {
        expr: Box<Expr>,
        // ([BOTH | LEADING | TRAILING], <expr>)
        trim_where: Option<(TrimWhereField, Box<Expr>)>,
    },

DONE =>

    /// EXTRACT(DateTimeField FROM <expr>)
    Extract {
        field: DateTimeField,
        expr: Box<Expr>,
    },
    /// TRY_CAST an expression to a different data type e.g. `TRY_CAST(foo AS VARCHAR(123))`
    //  this differs from CAST in the choice of how to implement invalid conversions
    TryCast {
        expr: Box<Expr>,
        data_type: DataType,
    },

✨ support CASE statements

In order to have actual utility, the SQL engine needs some code constructs, CASE statements should be convertible to a UDF function which we can run in the eval step.

WC 28-FEB

  • Sample dataset with lists and structs

[FEATURE] (sources) scan planning

Move the scan planning to the init for the scanner.

This will allow us to access the expected rows and other statistics when optimizing.

We can then use the expected rows to determine which dataset to hold in memory when joining.

This should be the information returned by DESCRIBE dataset which means changes to the writer to record more information.

Per column

Name
Type
Nulls?
Unique
Min
Max
Description

The Planner should plan the reads, including which portions to read and which selections and projections to push down to the reader operator.

This should then be described in JSON (or similar), so it can be hashed and used to persist partial reads to enable caching of 'results'

[FEATURE] Reader should support mabel by_ segments (basic support)

Thanks for stopping by to let us know something could be better!

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

[FEATURE] Subqueries

Support for table subqueries is a MUST

SELECT results.account
FROM (SELECT * FROM Players) AS results;

Support for SELECT is a could

SELECT account, (SELECT mascot FROM Guilds WHERE Players.guild = id) AS player_mascot
FROM Players;

support for IN is a should

SELECT account
FROM users
WHERE account IN (SELECT account FROM billing WHERE paid = true)

[FEATURE] standardize temporal queries

https://www.sqlshack.com/temporal-tables-in-sql-server/

The FOR SYSTEM_TIME clause has many variations and options. It is further classified into four temporal sub-clauses. This provides a way to query the data across current and history tables.

AS OF
FROM TO
BETWEEN AND
CONTAINED IN ( , )
ALL

The AS OF clause is used when there is a need to rebuild the original state of the data and need to know the state it was at any specific time in the past. This is possible by specifying the date time as its input.

[FEATURE] Planner should plan the reads rather than the Reader

The Planner should plan the reads, including which portions to read and which selections and projections to push down to the reader operator.

This should then be described in JSON (or similar), so it can be hashed and used to persist partial reads to enable caching of 'results'

[FEATURE] Support ORDER BY RANDOM()

Thanks for stopping by to let us know something could be better!

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

[FEATURE] Support $PARTITION hints

SELECT * FROM $satellites WHERE $PARTITION.username('green')

This would instruct the engine to try (it can ignore if it wants to) to prune the by_username partitioning to the username=green partition.

WC 07-FEB

  • SQL HAVING
  • SQL ORDER BY
  • $DATE Filters - not ready to start

[BUG] RANDOM should be between 0 and 1, not 0 and 1000

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behaviour:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behaviour
A clear and concise description of what you expected to happen.

Screenshots or Logs
If applicable, add screenshots or logs to help explain your problem.
!! Be aware not to publish any sensitive information in what you upload.

Environment (please complete the following information):

  • OS: [e.g. iOS]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

[FEATURE] non SELECT queries

> show tables;
+---------------+--------------------+------------+------------+
| table_catalog | table_schema       | table_name | table_type |
+---------------+--------------------+------------+------------+
| datafusion    | public             | t          | BASE TABLE |
| datafusion    | information_schema | tables     | VIEW       |
+---------------+--------------------+------------+------------+
> show columns from t;
+---------------+--------------+------------+-------------+-----------+-------------+
| table_catalog | table_schema | table_name | column_name | data_type | is_nullable |
+---------------+--------------+------------+-------------+-----------+-------------+
| datafusion    | public       | t          | a           | Int32     | NO          |
| datafusion    | public       | t          | b           | Utf8      | NO          |
| datafusion    | public       | t          | c           | Float32   | NO          |
+---------------+--------------+------------+-------------+-----------+-------------+

[FEATURE] support `CROSS JOIN` and `UNNEST`

This allows columns that are arrays, like a list of references, to be filtered on like this:

SELECT * FROM findings CROSS JOIN UNNEST(cves) as cve WHERE cve = 'CVE-2017-0144

or implied CROSS JOIN like this:

SELECT * FROM findings, UNNEST(cves) as cve WHERE cve = 'CVE-2017-0144

see:
https://medium.com/firebase-developers/using-the-unnest-function-in-bigquery-to-analyze-event-parameters-in-analytics-fb828f890b42

SELECT * FROM UNNEST(['foo', 'bar', 'baz', 'qux', 'corge', 'garply', 'waldo', 'fred']) AS element

[FEATURE] struct and list access

SELECT Birth_Place['state'] FROM $astronauts

"projection": [
            {
              "UnnamedExpr": {
                "MapAccess": {
                  "column": {
                    "Identifier": {
                      "value": "Birth_Place",
                      "quote_style": null
                    }
                  },
                  "keys": [
                    {
                      "Value": {
                        "SingleQuotedString": "state"
                      }
                    }
                  ]
                }
              }
            }
          ]

SELECT Misions[0] FROM $astronauts

"MapAccess": {
                  "column": {
                    "Identifier": {
                      "value": "Misions",
                      "quote_style": null
                    }
                  },
                  "keys": [
                    {
                      "Value": {
                        "Number": [
                          "0",
                          false
                        ]
                      }
                    }

✨ support UNION statements

UNION wil likely require changes to the parser as it may need to dynamically get the statement being worked on from a list, rather than hard-coded to 0

[FEATURE] JOINS

  • CROSS JOIN
  • JOIN
  • INNER JOIN _ ON
  • INNER JOIN _ USING
  • LEFT JOIN _ ON _

[FEATURE] Documentation

Some documentation has typos, is incomplete or incorrect.

Documentation is in different formats and isn't in the desired structure

[FEATURE] (sources) Execution plan DAG

The current DAG allows for an arbitrary set of unordered in coming edges, which means the planner can't include planning order or left/right nodes. This should be rewritten to have an order to the incoming edges (but not all nodes need to care)

Where Query planners are currently passed to nodes, these should be relations to other nodes.

The Node execution start at the final node and pull chunks through the DAG.

[FEATURE] ephemeral tables (VALUES)

SELECT *
FROM (VALUES (1,2),(3,4),(340,455)) AS t(a,b)

Allow tables to be defined in the SQL which don't need to be backed by a on disk dataset.

The will be useful for small look ups, e.g. to order by an ordinal field like HIGH, MEDIUM, LOW, or to unify nomenclature like TRUE, YES, ON

[FEATURE] Improve Performance of ORDER BY RANDOM()

This currently works by assigning a random number to each row and then sorting by the new column, what would probably be faster is creating a 0..n list, where n is the number of rows, shuffling this list and then using .take() to get the shuffled rows (in chunks)

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.