Skip to content

Commit

Permalink
Merge 41daec9 into 4a97610
Browse files Browse the repository at this point in the history
  • Loading branch information
jmeulemans committed Dec 13, 2018
2 parents 4a97610 + 41daec9 commit d5d1bda
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 22 deletions.
49 changes: 45 additions & 4 deletions graphql_compiler/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# Copyright 2017-present Kensho Technologies, LLC.
"""Commonly-used functions and data types from this package."""
from .compiler import CompilationResult, OutputMetadata # noqa
from .compiler import compile_graphql_to_gremlin, compile_graphql_to_match # noqa
from .compiler import ( # noqa
CompilationResult,
OutputMetadata,
compile_graphql_to_gremlin,
compile_graphql_to_match,
compile_graphql_to_sql,
)
from .query_formatting import insert_arguments_into_query # noqa
from .query_formatting.graphql_formatting import pretty_print_graphql # noqa

Expand All @@ -22,7 +27,7 @@ def graphql_to_match(schema, graphql_query, parameters, type_equivalence_hints=N
Args:
schema: GraphQL schema object describing the schema of the graph to be queried
graphql_string: the GraphQL query to compile to MATCH, as a string
graphql_query: the GraphQL query to compile to MATCH, as a string
parameters: dict, mapping argument name to its value, for every parameter the query expects.
type_equivalence_hints: optional dict of GraphQL interface or type -> GraphQL union.
Used as a workaround for GraphQL's lack of support for
Expand Down Expand Up @@ -53,12 +58,48 @@ def graphql_to_match(schema, graphql_query, parameters, type_equivalence_hints=N
query=insert_arguments_into_query(compilation_result, parameters))


def graphql_to_sql(schema, graphql_query, parameters, compiler_metadata,
type_equivalence_hints=None):
"""Compile the GraphQL input using the schema into a SQL query and associated metadata.
Args:
schema: GraphQL schema object describing the schema of the graph to be queried
graphql_query: the GraphQL query to compile to SQL, as a string
parameters: dict, mapping argument name to its value, for every parameter the query expects.
compiler_metadata: CompilerMetadata object, provides SQLAlchemy specific backend
information
type_equivalence_hints: optional dict of GraphQL interface or type -> GraphQL union.
Used as a workaround for GraphQL's lack of support for
inheritance across "types" (i.e. non-interfaces), as well as a
workaround for Gremlin's total lack of inheritance-awareness.
The key-value pairs in the dict specify that the "key" type
is equivalent to the "value" type, i.e. that the GraphQL type or
interface in the key is the most-derived common supertype
of every GraphQL type in the "value" GraphQL union.
Recursive expansion of type equivalence hints is not performed,
and only type-level correctness of this argument is enforced.
See README.md for more details on everything this parameter does.
*****
Be very careful with this option, as bad input here will
lead to incorrect output queries being generated.
*****
Returns:
a CompilationResult object, containing:
- query: string, the resulting compiled and parameterized query string
- language: string, specifying the language to which the query was compiled
- output_metadata: dict, output name -> OutputMetadata namedtuple object
- input_metadata: dict, name of input variables -> inferred GraphQL type, based on use
"""
raise NotImplementedError(u'Compiling GraphQL to SQL is not yet supported.')


def graphql_to_gremlin(schema, graphql_query, parameters, type_equivalence_hints=None):
"""Compile the GraphQL input using the schema into a Gremlin query and associated metadata.
Args:
schema: GraphQL schema object describing the schema of the graph to be queried
graphql_string: the GraphQL query to compile to Gremlin, as a string
graphql_query: the GraphQL query to compile to Gremlin, as a string
parameters: dict, mapping argument name to its value, for every parameter the query expects.
type_equivalence_hints: optional dict of GraphQL interface or type -> GraphQL union.
Used as a workaround for GraphQL's lack of support for
Expand Down
9 changes: 7 additions & 2 deletions graphql_compiler/compiler/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Copyright 2017-present Kensho Technologies, LLC.
from .common import CompilationResult, compile_graphql_to_gremlin, compile_graphql_to_match # noqa
from .common import GREMLIN_LANGUAGE, MATCH_LANGUAGE # noqa
from .common import ( # noqa
CompilationResult,
compile_graphql_to_gremlin,
compile_graphql_to_match,
compile_graphql_to_sql,
)
from .common import GREMLIN_LANGUAGE, MATCH_LANGUAGE, SQL_LANGUAGE # noqa
from .compiler_frontend import OutputMetadata # noqa
54 changes: 49 additions & 5 deletions graphql_compiler/compiler/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

MATCH_LANGUAGE = 'MATCH'
GREMLIN_LANGUAGE = 'Gremlin'
SQL_LANGUAGE = 'SQL'


def compile_graphql_to_match(schema, graphql_string, type_equivalence_hints=None):
Expand Down Expand Up @@ -47,7 +48,7 @@ def compile_graphql_to_match(schema, graphql_string, type_equivalence_hints=None

return _compile_graphql_generic(
MATCH_LANGUAGE, lowering_func, query_emitter_func,
schema, graphql_string, type_equivalence_hints)
schema, graphql_string, type_equivalence_hints, None)


def compile_graphql_to_gremlin(schema, graphql_string, type_equivalence_hints=None):
Expand Down Expand Up @@ -80,20 +81,63 @@ def compile_graphql_to_gremlin(schema, graphql_string, type_equivalence_hints=No

return _compile_graphql_generic(
GREMLIN_LANGUAGE, lowering_func, query_emitter_func,
schema, graphql_string, type_equivalence_hints)
schema, graphql_string, type_equivalence_hints, None)


def compile_graphql_to_sql(schema, graphql_string, compiler_metadata, type_equivalence_hints=None):
"""Compile the GraphQL input using the schema into a SQL query and associated metadata.
Args:
schema: GraphQL schema object describing the schema of the graph to be queried
graphql_string: the GraphQL query to compile to SQL, as a string
compiler_metadata: SQLAlchemy metadata containing tables for use during compilation.
type_equivalence_hints: optional dict of GraphQL interface or type -> GraphQL union.
Used as a workaround for GraphQL's lack of support for
inheritance across "types" (i.e. non-interfaces), as well as a
workaround for Gremlin's total lack of inheritance-awareness.
The key-value pairs in the dict specify that the "key" type
is equivalent to the "value" type, i.e. that the GraphQL type or
interface in the key is the most-derived common supertype
of every GraphQL type in the "value" GraphQL union.
Recursive expansion of type equivalence hints is not performed,
and only type-level correctness of this argument is enforced.
See README.md for more details on everything this parameter does.
*****
Be very careful with this option, as bad input here will
lead to incorrect output queries being generated.
*****
Returns:
a CompilationResult object
"""
raise NotImplementedError(u'Compiling GraphQL to SQL is not yet supported.')


def _compile_graphql_generic(language, lowering_func, query_emitter_func,
schema, graphql_string, type_equivalence_hints):
"""Compile the GraphQL input, lowering and emitting the query using the given functions."""
schema, graphql_string, type_equivalence_hints, compiler_metadata):
"""Compile the GraphQL input, lowering and emitting the query using the given functions.
Args:
language: string indicating the target language to compile to.
lowering_func: Function to lower the compiler IR into a compatible form for the target
language backend.
query_emitter_func: Function that emits a query in the target language from the lowered IR.
schema: GraphQL schema object describing the schema of the graph to be queried.
graphql_string: the GraphQL query to compile to the target language, as a string.
type_equivalence_hints: optional dict of GraphQL interface or type -> GraphQL union.
compiler_metadata: optional target specific metadata for usage by the query_emitter_func.
Returns:
a CompilationResult object
"""
ir_and_metadata = graphql_to_ir(
schema, graphql_string, type_equivalence_hints=type_equivalence_hints)

lowered_ir_blocks = lowering_func(
ir_and_metadata.ir_blocks, ir_and_metadata.query_metadata_table,
type_equivalence_hints=type_equivalence_hints)

query = query_emitter_func(lowered_ir_blocks)
query = query_emitter_func(lowered_ir_blocks, compiler_metadata)

return CompilationResult(
query=query,
Expand Down
2 changes: 1 addition & 1 deletion graphql_compiler/compiler/emit_gremlin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Public API #
##############

def emit_code_from_ir(ir_blocks):
def emit_code_from_ir(ir_blocks, compiler_metadata):
"""Return a MATCH query string from a list of IR blocks."""
gremlin_steps = (
block.to_gremlin()
Expand Down
2 changes: 1 addition & 1 deletion graphql_compiler/compiler/emit_match.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def emit_code_from_multiple_match_queries(match_queries):
return u' '.join(query_data)


def emit_code_from_ir(compound_match_query):
def emit_code_from_ir(compound_match_query, compiler_metadata):
"""Return a MATCH query string from a CompoundMatchQuery."""
# If the compound match query contains only one match query,
# just call `emit_code_from_single_match_query`
Expand Down
7 changes: 7 additions & 0 deletions graphql_compiler/compiler/emit_sql.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright 2018-present Kensho Technologies, LLC.
"""Transform a tree representation of an SQL query into an executable SQLAlchemy query."""


def emit_code_from_ir(sql_query_tree, compiler_metadata):
"""Return a SQLAlchemy Query from a passed tree representation of an SQL query."""
raise NotImplementedError(u'SQL query emitting is not yet supported.')
35 changes: 35 additions & 0 deletions graphql_compiler/compiler/ir_lowering_sql/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2018-present Kensho Technologies, LLC.

##############
# Public API #
##############


def lower_ir(ir_blocks, query_metadata_table, type_equivalence_hints=None):
"""Lower the IR into a form that can be represented by a SQL query.
Args:
ir_blocks: list of IR blocks to lower into SQL-compatible form
query_metadata_table: QueryMetadataTable object containing all metadata collected during
query processing, including location metadata (e.g. which locations
are folded or optional).
type_equivalence_hints: optional dict of GraphQL interface or type -> GraphQL union.
Used as a workaround for GraphQL's lack of support for
inheritance across "types" (i.e. non-interfaces), as well as a
workaround for Gremlin's total lack of inheritance-awareness.
The key-value pairs in the dict specify that the "key" type
is equivalent to the "value" type, i.e. that the GraphQL type or
interface in the key is the most-derived common supertype
of every GraphQL type in the "value" GraphQL union.
Recursive expansion of type equivalence hints is not performed,
and only type-level correctness of this argument is enforced.
See README.md for more details on everything this parameter does.
*****
Be very careful with this option, as bad input here will
lead to incorrect output queries being generated.
*****
Returns:
tree representation of IR blocks for recursive traversal by SQL backend.
"""
raise NotImplementedError(u'SQL IR lowering is not yet implemented.')
18 changes: 9 additions & 9 deletions graphql_compiler/tests/test_emit_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_simple_immediate_output(self):
)
'''

received_match = emit_match.emit_code_from_ir(compound_match_query)
received_match = emit_match.emit_code_from_ir(compound_match_query, None)
compare_match(self, expected_match, received_match)

def test_simple_traverse_filter_output(self):
Expand Down Expand Up @@ -91,7 +91,7 @@ def test_simple_traverse_filter_output(self):
)
'''

received_match = emit_match.emit_code_from_ir(compound_match_query)
received_match = emit_match.emit_code_from_ir(compound_match_query, None)
compare_match(self, expected_match, received_match)

def test_output_inside_optional_traversal(self):
Expand Down Expand Up @@ -159,7 +159,7 @@ def test_output_inside_optional_traversal(self):
)
'''

received_match = emit_match.emit_code_from_ir(compound_match_query)
received_match = emit_match.emit_code_from_ir(compound_match_query, None)
compare_match(self, expected_match, received_match)

def test_datetime_variable_representation(self):
Expand Down Expand Up @@ -201,7 +201,7 @@ def test_datetime_variable_representation(self):
)
'''

received_match = emit_match.emit_code_from_ir(compound_match_query)
received_match = emit_match.emit_code_from_ir(compound_match_query, None)
compare_match(self, expected_match, received_match)

def test_datetime_output_representation(self):
Expand All @@ -228,7 +228,7 @@ def test_datetime_output_representation(self):
)
'''

received_match = emit_match.emit_code_from_ir(compound_match_query)
received_match = emit_match.emit_code_from_ir(compound_match_query, None)
compare_match(self, expected_match, received_match)


Expand Down Expand Up @@ -257,7 +257,7 @@ def test_simple_immediate_output(self):
])}
'''

received_match = emit_gremlin.emit_code_from_ir(ir_blocks)
received_match = emit_gremlin.emit_code_from_ir(ir_blocks, None)
compare_gremlin(self, expected_gremlin, received_match)

def test_simple_traverse_filter_output(self):
Expand Down Expand Up @@ -292,7 +292,7 @@ def test_simple_traverse_filter_output(self):
])}
'''

received_match = emit_gremlin.emit_code_from_ir(ir_blocks)
received_match = emit_gremlin.emit_code_from_ir(ir_blocks, None)
compare_gremlin(self, expected_gremlin, received_match)

def test_output_inside_optional_traversal(self):
Expand Down Expand Up @@ -331,7 +331,7 @@ def test_output_inside_optional_traversal(self):
])}
'''

received_match = emit_gremlin.emit_code_from_ir(ir_blocks)
received_match = emit_gremlin.emit_code_from_ir(ir_blocks, None)
compare_gremlin(self, expected_gremlin, received_match)

def test_datetime_output_representation(self):
Expand All @@ -354,5 +354,5 @@ def test_datetime_output_representation(self):
])}
'''

received_match = emit_gremlin.emit_code_from_ir(ir_blocks)
received_match = emit_gremlin.emit_code_from_ir(ir_blocks, None)
compare_gremlin(self, expected_gremlin, received_match)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ funcy==1.7.3
graphql-core==1.1
pytz==2017.2
six==1.10.0
sqlalchemy==1.2.9
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def find_long_description():
'graphql-core==1.1',
'pytz>=2016.10',
'six>=1.10.0',
'sqlalchemy>=1.2.1,<1.3',
],
classifiers=[
'Development Status :: 5 - Production/Stable',
Expand Down

0 comments on commit d5d1bda

Please sign in to comment.