Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

suggestion: let me construct the query myself. #129

Open
matantsu opened this issue Apr 14, 2015 · 9 comments
Open

suggestion: let me construct the query myself. #129

matantsu opened this issue Apr 14, 2015 · 9 comments

Comments

@matantsu
Copy link

in a query_method , when you specify query_fields the library automatically adds a filter of equallity
I have found that in many uses you wouldn't nesseceraly want to have an equality filter in there.

examples:

  • searching for past entries (date < now)
  • partial string matching (ndb.AND(Kind.prop >= 'abc' , Kind.prop <= 'abcz'))

i have found a workaround , but it involves accessing a private property.

models.Product.query_method(
        name='product.search',
        path='product/search',
        query_fields=('search_term',)
    )
    ### this searches for products by name.
    def product_search(self, q): 
        ### getting the search term from the `FilterNode` Object.
        term = q.filters._FilterNode__value 
        ### ditching the original query and constructing a new one. 
        q = models.Product.query() 
        return q.filter(ndb.AND(models.Product.name >= term,models.Product.name <= term + 'z'))
@dhermes
Copy link
Contributor

dhermes commented Apr 14, 2015

When you say "let me construct the query myself", what do you mean?

Having >= or other non-equality filters can't happen in a query string because it just isn't part of the spec and we aren't going to re-invent the wheel.

To get custom behavior, you're better off using an EndpointsAliasProperty and then augment the query in the setter.

For example, from an application I wrote in 2013:

    def LastUpdatedSet(self, value):
        last_updated = utils.DatetimeValueFromString(value)
        self._endpoints_query_info._filters.add(Photo.updated >= last_updated)

@matantsu
Copy link
Author

I mean , just provide me with the arguments (in this case it is the search_term) and I will handle the query myself. the way the library is constructed now is not giving me the choise for the comparison method.

using _endpoints_query_info , I presume would add this filter for all query_methods ? if so than it is not the behaviour I want. (it is still good for things like fake deleting , where you still keep the entity but just flag it not to be retreived)

@dhermes
Copy link
Contributor

dhermes commented Apr 14, 2015

Using _endpoints_query_info would add the filter for all query_methods that used an alias property explicitly made for doing the comparison query.

With a clear name like updatedSince, this should not be an issue.


I am thinking about ways to get the query info back in a simple way.

You could hack this now by using method and then using custom message definitions for the way in and the way out.

query_in = Product.ProtoModel(fields=...)
query_out = Product.ProtoCollection(collection_fields=...)
@Product.method(request_message=query_in, response_message=query_out, ...)
def my_query(self, req_obj):
    # turn the request obj req_obj into a query
    query = do_something(req_obj)
    items, _, _ = query.fetch_page(some_limit)
    # Turn the items into the necessary output of type `query_out`
    result = do_something_else(items)
    return result

@matantsu
Copy link
Author

If so , using _endpoints_query_info seems like a good choise , even though you clutter up your model with aliases. I think this kind of behavior may cover 90 % of use cases.

As for your example , of course I could do anything I want by just setting custom request_message and response_mesaage , but then why have query_method ?

@dhermes
Copy link
Contributor

dhermes commented Apr 15, 2015

I'm happy to leave this open and discuss. I think it's a legitimate thing to want.

As for using (request|response)_message, I agree it is not particularly useful, just wanted to give you a half-way point if you wanted.

@matantsu matantsu reopened this Apr 16, 2015
@maxandron
Copy link

Although this is an old issue I would love to jump in to the discussion as I just encountered a frustrating use case.
My use case is when I want to create a complex query constructed from a AliasProperty.
It is easier to explain in code:

def foo_set(self, foo_index):
    if 1 == foo_index:
        # FooModel.id should be one of 1, 2, 3
    else:
        # FooModel.id == 5 OR FooModel.id == 212

@EndpointsAliasProperty(name='foo_id', setter=foo_set)
def foo_id(self):
    ...

Using self._endpoints_query_info._filters.add only allows one simple filter. (No OR or AND queries)

Currently the only thing I can do is not use endpoints-proto-datastore for the specific model.

EDIT: Saw it was already accepted in #59. Is there any work around currently?

@dhermes
Copy link
Contributor

dhermes commented Sep 14, 2015

@maxandron Thanks for tuning in! There is no work around currently.

Any requests / suggestions for how this behavior might be enabled?

@maxandron
Copy link

Currently the way I solved it (instead of writing the logic without using your library - which does make life a lot easier), is add the "query" as an additional header.
And then accessing it using self.request_state.headers.get('query').
Then I just build the filter manually.

This is not recommended though and I will think of another solution and post it here.

@dhermes
Copy link
Contributor

dhermes commented Sep 15, 2015

Thanks. I look forward to it!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants