Thanks a lot for the explanation, it's all clear now! On 15 May 2017 at 18:56, mike bayer <mike...@zzzcomputing.com> wrote: > > > On 05/15/2017 11:56 AM, Zsolt Ero wrote: >> >> I might not be understanding something, but for me there are two >> different concepts here: >> >> map_obj = dbsession.query(Map).get(id_) >> >> is an object in memory, loaded with a long SELECT statement, allowing >> us to get and set different attributes and the session / transaction >> manager commits the auto-detected changes. >> >> Whereas with >> >> dbsession.query(Map).filter(Map.id == id_).update( >> {"screenshots": func.jsonb_set(Map.screenshots, '{size}', >> '"filename.jpg"')}, synchronize_session='fetch') >> >> there is no object in memory, what we are writing here is just a nicer >> syntax for a one line SQL UPDATE query. Even the triggered SELECT >> statement is just querying for a single .id, which we have anyway. > > > the purpose of synchronize_session is only if you happened to run *both* > Python statements, so that you have map_obj present as a local variable, and > wish to expire the now stale value of map_obj.screenshots, so that when you > next access it, a SELECT is emitted to get the most recent value, e.g. the > one that's the result of your UPDATE statement. > > the "fetch" strategy is actually wasteful here because it runs the query() > as a SELECT in order to locate the primary keys of the objects that might be > locally present, but your query is simple enough that this is already > apparent. the "fetch" strategy currently doesn't even bother to get the new > value right now and just expires, I forgot about this. this is why you see > just the one wasteful SELECT statement. "fetch" probably should be > improved to actually fetch and directly update the values for the instances > it locates, not sure why it wasn't done that way to start. > > the "evaluate" strategy does everything in Python, but also won't work > because it currently expects that the values which were set are also > evaluatable in Python, also should be improved to at least do a simple > "expire" for attributes that can't be evaluated in Python. > > in this case the only strategy left is sychronize_session=False. However if > you have map_obj in memory, you'd need to run refresh() on it to get the new > JSON value if you care about it. > > > > > > >> >> Zsolt >> >> >> >> >> On 15 May 2017 at 17:29, mike bayer <mike...@zzzcomputing.com> wrote: >>> >>> >>> >>> On 05/15/2017 10:54 AM, Zsolt Ero wrote: >>>> >>>> >>>> Thanks, it is all clear now. Just out of interest, what is the point >>>> of synchronize_session='fetch'? >>> >>> >>> >>> that will do a SELECT and get the new value back and update your ORM >>> object >>> in memory. Set synchronize_session=False if you don't care. >>> >>> >>> >>>> >>>> For me all it does is a simple SELECT maps.id AS maps_id FROM maps >>>> WHERE maps.id = %(id_1)s >>>> >>>> All I get as a return value is 0: not successful (probably id didn't >>>> exist), while 1: successful. It is the same behaviour both with >>>> 'fetch' and False. >>>> >>>> Zsolt >>>> >>>> On 15 May 2017 at 16:33, mike bayer <mike...@zzzcomputing.com> wrote: >>>>> >>>>> >>>>> >>>>> >>>>> On 05/15/2017 10:31 AM, Zsolt Ero wrote: >>>>>> >>>>>> >>>>>> >>>>>> I'm trying to run your example, but it doesn't work: >>>>>> >>>>>> from sqlalchemy import func >>>>>> >>>>>> m = request.dbsession.query(models.Map).get(3) >>>>>> m.screenshots = func.jsonb_set(m.screenshots, '{key}', '"value"') >>>>>> request.dbsession.flush() >>>>>> >>>>>> It ends up in a (psycopg2.ProgrammingError) can't adapt type 'dict'. >>>>> >>>>> >>>>> >>>>> >>>>> jsonb_set(models.Map.screenshots, ...) >>>>> >>>>> because this works against the column, not the value >>>>> >>>>> >>>>> >>>>> >>>>> >>>>>> >>>>>> Also, from the generated SQL it seems to me that it's also doing the >>>>>> full JSONB update from client side, not just inserting a key into the >>>>>> database server side. >>>>>> >>>>>> UPDATE maps SET screenshots=jsonb_set(%(jsonb_set_1)s, >>>>>> %(jsonb_set_2)s, %(jsonb_set_3)s) WHERE maps.id = %(maps_id)s >>>>>> {'maps_id': 3, 'jsonb_set_3': '"value"', 'jsonb_set_2': '{key}', >>>>>> 'jsonb_set_1': {u'small': u'2ad139ee69cdcd9e.jpg', u'full': >>>>>> u'68b3f51491ff1501.jpg'}} >>>>>> >>>>>> On 15 May 2017 at 16:18, Zsolt Ero <zsolt....@gmail.com> wrote: >>>>>>> >>>>>>> >>>>>>> >>>>>>> Thanks for the answer. My use case is the following: >>>>>>> >>>>>>> I have an object (map_obj), which has screenshots in two sizes. I'm >>>>>>> using JSONB columns to store the screenshot filenames. >>>>>>> >>>>>>> Now, the two screenshot sizes are generated in parallel. The code is >>>>>>> like the following: >>>>>>> >>>>>>> map_obj = query(...by id...) >>>>>>> filename = generate_screenshot(size) # long running screenshot >>>>>>> generation >>>>>>> >>>>>>> try: >>>>>>> dbsession.refresh(map_obj, ['screenshots']) >>>>>>> map_obj.screenshots = dict(map_obj.screenshots, **{size: >>>>>>> filename}) >>>>>>> except Exception as e: >>>>>>> logger.warning(...) >>>>>>> >>>>>>> It worked well for 99.9% of the cases. The problem is that in the >>>>>>> rare >>>>>>> case when both screenshots got rendered within a few milliseconds, >>>>>>> one >>>>>>> of the screenshots got lost. >>>>>>> >>>>>>> The simple solution was to add lockmode='update' to the refresh, so >>>>>>> this way the refreshes are blocking until the other finishes the >>>>>>> update. >>>>>>> >>>>>>> But since this means locking a full row, I was thinking a simple >>>>>>> JSONB >>>>>>> insertion would probably be better, since I can avoid locking the >>>>>>> row. >>>>>>> >>>>>>> Zsolt >>>>>>> >>>>>>> >>>>>>> >>>>>>> >>>>>>> On 15 May 2017 at 15:58, mike bayer <mike...@zzzcomputing.com> wrote: >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> On 05/15/2017 09:32 AM, Zsolt Ero wrote: >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> In PostgreSQL 9.5+ it is finally possible to modify a single key >>>>>>>>> inside >>>>>>>>> a >>>>>>>>> JSONB column. Usage is something like this: >>>>>>>>> >>>>>>>>> update maps set screenshots=jsonb_set(screenshots, '{key}', >>>>>>>>> '"value"') >>>>>>>>> where id = 10688 >>>>>>>>> >>>>>>>>> Is it possible to write this query using the ORM somehow? If not, >>>>>>>>> please >>>>>>>>> take it as a feature request. >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> You can use that function directly: >>>>>>>> >>>>>>>> my_object = session.query(Maps).get(5) >>>>>>>> >>>>>>>> my_object.screenshots = func.jsonb_set(my_object.screenshots, >>>>>>>> '{key}', >>>>>>>> '"value"') >>>>>>>> >>>>>>>> session.flush() >>>>>>>> >>>>>>>> >>>>>>>> as far as "transparent" ORM use of that, like this: >>>>>>>> >>>>>>>> my_object.screenshots[key] = "value" >>>>>>>> >>>>>>>> right now that is a mutation of the value, and assuming you were >>>>>>>> using >>>>>>>> MutableDict to detect this as an ORM change event, the ORM considers >>>>>>>> "screenshots" to be a single value that would be the target of an >>>>>>>> UPDATE, >>>>>>>> meaning the whole JSON dictionary is passed into the UPDATE. There >>>>>>>> is >>>>>>>> no >>>>>>>> infrastructure for the ORM to automatically turn certain column >>>>>>>> updates >>>>>>>> into >>>>>>>> finely-detailed SQL function calls. I can imagine that there might >>>>>>>> be >>>>>>>> some >>>>>>>> event-based way to make this happen transparently within the flush, >>>>>>>> however, >>>>>>>> but I'd need to spend some time poking around to work out how that >>>>>>>> might >>>>>>>> work. >>>>>>>> >>>>>>>> >>>>>>>> I'm not familiar with what the advantage to jsonb_set() would be and >>>>>>>> I >>>>>>>> can >>>>>>>> only guess it's some kind of performance advantage. I'd be curious >>>>>>>> to >>>>>>>> see >>>>>>>> under what scenarios being able to set one element of the JSON vs. >>>>>>>> UPDATEing >>>>>>>> the whole thing is a performance advantage significant compared to >>>>>>>> the >>>>>>>> usual >>>>>>>> overhead of the ORM flush process; that is, Postgresql is really >>>>>>>> fast, >>>>>>>> and >>>>>>>> for this optimization to be significant, you probably need to be >>>>>>>> calling >>>>>>>> the >>>>>>>> Core function directly anyway rather than going through the whole >>>>>>>> ORM >>>>>>>> flush >>>>>>>> process. But this is all based on my assumption as to what your >>>>>>>> goal >>>>>>>> of >>>>>>>> using this function is. >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> -- >>>>>>>>> SQLAlchemy - >>>>>>>>> The Python SQL Toolkit and Object Relational Mapper >>>>>>>>> >>>>>>>>> http://www.sqlalchemy.org/ >>>>>>>>> >>>>>>>>> To post example code, please provide an MCVE: Minimal, Complete, >>>>>>>>> and >>>>>>>>> Verifiable Example. See http://stackoverflow.com/help/mcve for a >>>>>>>>> full >>>>>>>>> description. >>>>>>>>> --- >>>>>>>>> You received this message because you are subscribed to the Google >>>>>>>>> Groups >>>>>>>>> "sqlalchemy" group. >>>>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>>>> send >>>>>>>>> an >>>>>>>>> email to sqlalchemy+unsubscr...@googlegroups.com >>>>>>>>> <mailto:sqlalchemy+unsubscr...@googlegroups.com>. >>>>>>>>> To post to this group, send email to sqlalchemy@googlegroups.com >>>>>>>>> <mailto:sqlalchemy@googlegroups.com>. >>>>>>>>> Visit this group at https://groups.google.com/group/sqlalchemy. >>>>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> -- >>>>>>>> SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper >>>>>>>> >>>>>>>> http://www.sqlalchemy.org/ >>>>>>>> >>>>>>>> To post example code, please provide an MCVE: Minimal, Complete, and >>>>>>>> Verifiable Example. See http://stackoverflow.com/help/mcve for a >>>>>>>> full >>>>>>>> description. >>>>>>>> --- You received this message because you are subscribed to a topic >>>>>>>> in >>>>>>>> the >>>>>>>> Google Groups "sqlalchemy" group. >>>>>>>> To unsubscribe from this topic, visit >>>>>>>> >>>>>>>> https://groups.google.com/d/topic/sqlalchemy/hjjIyEC8KHQ/unsubscribe. >>>>>>>> To unsubscribe from this group and all its topics, send an email to >>>>>>>> sqlalchemy+unsubscr...@googlegroups.com. >>>>>>>> To post to this group, send email to sqlalchemy@googlegroups.com. >>>>>>>> Visit this group at https://groups.google.com/group/sqlalchemy. >>>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>> >>>>>> >>>>>> >>>>>> >>>>> >>>>> -- >>>>> SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper >>>>> >>>>> http://www.sqlalchemy.org/ >>>>> >>>>> To post example code, please provide an MCVE: Minimal, Complete, and >>>>> Verifiable Example. See http://stackoverflow.com/help/mcve for a full >>>>> description. >>>>> --- You received this message because you are subscribed to a topic in >>>>> the >>>>> Google Groups "sqlalchemy" group. >>>>> To unsubscribe from this topic, visit >>>>> https://groups.google.com/d/topic/sqlalchemy/hjjIyEC8KHQ/unsubscribe. >>>>> To unsubscribe from this group and all its topics, send an email to >>>>> sqlalchemy+unsubscr...@googlegroups.com. >>>>> To post to this group, send email to sqlalchemy@googlegroups.com. >>>>> Visit this group at https://groups.google.com/group/sqlalchemy. >>>>> For more options, visit https://groups.google.com/d/optout. >>>> >>>> >>>> >>> >>> -- >>> SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper >>> >>> http://www.sqlalchemy.org/ >>> >>> To post example code, please provide an MCVE: Minimal, Complete, and >>> Verifiable Example. See http://stackoverflow.com/help/mcve for a full >>> description. >>> --- You received this message because you are subscribed to a topic in >>> the >>> Google Groups "sqlalchemy" group. >>> To unsubscribe from this topic, visit >>> https://groups.google.com/d/topic/sqlalchemy/hjjIyEC8KHQ/unsubscribe. >>> To unsubscribe from this group and all its topics, send an email to >>> sqlalchemy+unsubscr...@googlegroups.com. >>> To post to this group, send email to sqlalchemy@googlegroups.com. >>> Visit this group at https://groups.google.com/group/sqlalchemy. >>> For more options, visit https://groups.google.com/d/optout. >> >> > > -- > SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper > > http://www.sqlalchemy.org/ > > To post example code, please provide an MCVE: Minimal, Complete, and > Verifiable Example. See http://stackoverflow.com/help/mcve for a full > description. > --- You received this message because you are subscribed to a topic in the > Google Groups "sqlalchemy" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/sqlalchemy/hjjIyEC8KHQ/unsubscribe. > To unsubscribe from this group and all its topics, send an email to > sqlalchemy+unsubscr...@googlegroups.com. > To post to this group, send email to sqlalchemy@googlegroups.com. > Visit this group at https://groups.google.com/group/sqlalchemy. > For more options, visit https://groups.google.com/d/optout.
-- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.