-
Notifications
You must be signed in to change notification settings - Fork 229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to filter and limit? #27
Comments
If you use You can extrapolate how this works from https://facebook.github.io/relay/docs/graphql-connections.html#content |
awesome! thanks. It doesn't look like it's actually passing the params to SQLAlchemy, and rather filter/limiting the results after they are fetched from the db. Looking at graphene_sqlalchemy/fields.py its doing the full query and then calling connection_from_list_slice that then slices it given the params. Is there a particular reason that the query itself is not offset and limited? Or am I misunderstanding. Thank you. |
Is it? Looking at my logs I do see |
oh! you are right. I was subclassing SQLAlchemyConnectionField so that I could add the same parameter for all my objects and that was causing the problem. Here was the culprit code. As you can see I was trying to provide a way to have filter by uuid for all my objects, but obviously its not correct.
|
Any suggestions on how I can query my objects by uuid? |
Right now I am thinking that I have to add my own resolve function to the query. Is that right? |
I was able to do it this way:
Now I can do this:
that returns:
And do this:
that returns
Also I think I can extend SQLAlchemyConnectionField with a metaclass approach to provide filtering on any field! This is awesome!! Please let me know if this is a good approach. |
I am new to Graphene and GraphQL, and have been trying to figure out how to query by a single argument with graphene-sqlalchemy. Is the above solution the only way to query by argument? Does one have to use Relay with Graphene? I would prefer not to use Relay. Update: I figured it out. I had to add an argument to the object that is being returned by the query and that way the resolver would be able to pick it up:
|
|
Here's something I threw together that still supports the default relay params, and also allows filtering by any model attribute, including class MyConnectionField(graphene_sqlalchemy.SQLAlchemyConnectionField):
RELAY_ARGS = ['first', 'last', 'before', 'after']
@classmethod
def get_query(cls, model, context, info, args):
query = super(MyConnectionField, cls).get_query(model, context, info, args)
for field, value in args.items():
if field not in cls.RELAY_ARGS:
query = query.filter(getattr(model, field) == value)
return query
class Query(graphene.ObjectType):
node = graphene.relay.Node.Field()
all_somethings = MyConnectionField(SomethingNode, field_name=grahene.String()) |
@aminghadersohi Pretty sure your example is what I need, but it's difficult to wrap my head around with only a beginning knowledge of GraphQL and the Python supporting libraries! Honestly, I think this sort of functionality should be built into graphene-sqlalchemy or at least be a configuration option when instantiating the Metaclass. As it stands this library is really only useful for non-parameterized queries. |
@athal7 Looks like this does not work in latest releases with Graphene 2.0, downgrading it works. Any thoughts on how to fix it? Looking at upgrading to 2.0 docs nothing stood out. |
@kavink yes just a few changes: class MyConnectionField(graphene_sqlalchemy.SQLAlchemyConnectionField):
RELAY_ARGS = ['first', 'last', 'before', 'after']
@classmethod
- def get_query(cls, model, context, info, args):
- query = super(MyConnectionField, cls).get_query(model, context, info, args)
+ def get_query(cls, model, info, **args):
+ query = super(MyConnectionField, cls).get_query(model, info, **args)
for field, value in args.items():
if field not in cls.RELAY_ARGS:
query = query.filter(getattr(model, field) == value)
return query
class Query(graphene.ObjectType):
node = graphene.relay.Node.Field()
all_somethings = MyConnectionField(SomethingNode, field_name=grahene.String()) |
Here if someone needs exact and like query filtering:
And in Query class: |
So some of this works, but doesn't expose the right schema. Here was my solution:
|
Sorry, after testing I realized my solution was a little more incomplete than I intended and didn't preserve certain features. Here's a tested version. Note: from graphene_sqlalchemy import SQLAlchemyConnectionField
from graphene import Field, String, NonNull, List
from graphene.utils.str_converters import to_snake_case
from graphene.relay.connection import PageInfo
from graphql_relay.connection.arrayconnection import connection_from_list_slice
from sqlalchemy import desc, asc
ORDER_FUNCTIONS = {'asc': asc, 'desc': desc}
class InstrumentedQuery(SQLAlchemyConnectionField):
def __init__(self, type, **kwargs):
self.query_args = {}
for k, v in type._meta.fields.items():
if isinstance(v, Field):
field_type = v.type
if isinstance(field_type, NonNull):
field_type = field_type.of_type
self.query_args[k] = field_type()
args = kwargs.pop('args', dict())
args.update(self.query_args)
args['sort_by'] = List(String, required=False)
super().__init__(type, args=args, **kwargs)
def get_query(self, model, info, **args):
query_filters = {k: v for k, v in args.items() if k in self.query_args}
query = model.query.filter_by(**query_filters)
if 'sort_by' in args:
criteria = [self.get_order_by_criterion(model, *arg.split(' ')) for arg in args['sort_by']]
query = query.order_by(*criteria)
return query
def connection_resolver(self, resolver, connection, model, root, info, **args):
query = resolver(root, info, **args) or self.get_query(model, info, **args)
count = query.count()
connection = connection_from_list_slice(
query,
args,
slice_start=0,
list_length=count,
list_slice_length=count,
connection_type=connection,
pageinfo_type=PageInfo,
edge_type=connection.Edge,
)
connection.iterable = query
connection.length = count
return connection
@staticmethod
def get_order_by_criterion(model, name, direction='asc'):
return ORDER_FUNCTIONS[direction.lower()](getattr(model, to_snake_case(name))) |
@teruokun seems like your solution only does Also is it possible to also add usage of your class and an example of what user should expect when using ? |
Actually it does work for column-based data by analyzing the fields in the meta model and not only allows you to send them but also enforces correct typing (based on the output field types) as well as inclusion in the schema as arguments for the related query. It doesn't propagate to connections referenced through relationships, which is ideally what I'd want, but I've verified and tested that it does actually get evidenced in the schema properly. Basically, my solution works by creating a query that works for all fields, not just the ones you would specify as specific arguments (which may be too much magic, I grant you). Usage is the same way you'd use SQLAlchemyConnectionField to specify a query: class Query(graphene.ObjectType):
node = graphene.relay.Node.Field()
all_somethings = InstrumentedQuery(SomethingNode) |
@teruokun Thanks , Your work has been very helpful to me. One other issue i'm trying to resolve is .
For But when going from Any thoughts on how can make InstrumentedQuery available for relationships too ? So I can filter by columns of |
Yeah, I was looking into that myself and couldn't figure out exactly how to get it to work. I think I'd need some additional help troubleshooting from one of the core developers because I was trying to register it as the default connection field factory using the |
NOTE: not fully tested at all. I'll update the code here if i find anything terribly wrong with it. Essentially it allows you add a JSON field type argument "filters" to a custom connection field. example OR gql query:
Dynamic Filtering Connection Field:
Example query/resolver:
|
Has anyone being able to create grapqhl schema for |
How can I get the original ID value in the response instead of base64 encoded value?
Expecting value:
|
@interpegasus This is the graphene-sqlalchemy repository and not the graphene-django repo, so unfortunately not applicable... |
Hi all, |
This works great for smaller datasets, but doesnt seem to scale well for large datasets (1M+rows) What I'm trying to do is to expose a few tables via graphql so that its easier for end users to query by passing filters and and use pagination to handle resultset efficiently for large tables.
|
Is this still not built into the graphene-sqlalchemy library? This comments solution is what I was able to get to work, but wasn't sure if there was a more appropriate/recommended solution that might be built in that I might be missing. |
If someone like me is tired of waiting for filtering, then you can use graphene-sqlalchemy-filter. |
Is there a reason for not having an offset? Is using |
As I understand the idea is to follow relay specification that has advantage of opaque cursors, where when using limit/offset you end up jumping over the data or having them twice in the successive responses. |
Closing all [duplicate] issues related to this. The discussion will continue in #347 (WIP). A proposal for the implementation of filters is currently being worked on and will be posted there once it is ready. |
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related topics referencing this issue. |
Hey really nice library. Does it support filtering and limit/offset?
The text was updated successfully, but these errors were encountered: