)]}'
{"nova/db/sqlalchemy/api.py":[{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"de933d0c7bb7e349b5888dd99277d101d7198c8e","unresolved":false,"context_lines":[{"line_number":2043,"context_line":"        tags \u003d filters.pop(\u0027not-tags\u0027)"},{"line_number":2044,"context_line":"        query_prefix \u003d query_prefix.join(models.Instance.tags)"},{"line_number":2045,"context_line":"        query_prefix \u003d query_prefix.filter(~models.Instance.tags.any("},{"line_number":2046,"context_line":"            models.Tag.tag.in_(tags)))"},{"line_number":2047,"context_line":""},{"line_number":2048,"context_line":"    if \u0027not-tags-any\u0027 in filters:"},{"line_number":2049,"context_line":"        tags \u003d filters.pop(\u0027not-tags-any\u0027)"}],"source_content_type":"text/x-python","patch_set":1,"id":"ba8a016a_aa8fe721","line":2046,"updated":"2015-11-23 17:07:44.000000000","message":"This is actually implementing the not-tags-any expression. The above produces this SQL:\n\n SELECT ... FROM instances i\n JOIN tags t ON i.uuid \u003d t.resource_id\n WHERE t.tag NOT IN (T1, T2);\n\nWhich returns any instance that does not have either the T1 or T2 tag.","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"a2cd0c3686d274f9a6a89fd28a253c759816b4c4","unresolved":false,"context_lines":[{"line_number":2043,"context_line":"        tags \u003d filters.pop(\u0027not-tags\u0027)"},{"line_number":2044,"context_line":"        query_prefix \u003d query_prefix.join(models.Instance.tags)"},{"line_number":2045,"context_line":"        query_prefix \u003d query_prefix.filter(~models.Instance.tags.any("},{"line_number":2046,"context_line":"            models.Tag.tag.in_(tags)))"},{"line_number":2047,"context_line":""},{"line_number":2048,"context_line":"    if \u0027not-tags-any\u0027 in filters:"},{"line_number":2049,"context_line":"        tags \u003d filters.pop(\u0027not-tags-any\u0027)"}],"source_content_type":"text/x-python","patch_set":1,"id":"9a8ffd7b_eaaa1d85","line":2046,"in_reply_to":"ba8a016a_03f7e08b","updated":"2015-11-24 17:37:06.000000000","message":"You are correct that the above will produce the following semantic SQL:\n\n SELECT ... FROM instances i\n JOIN tags t ON i.uuid \u003d t.resource_id\n WHERE NOT EXISTS (\n   SELECT 1 FROM tags WHERE resource_id \u003d i.uuid AND tag IN (T1, T2)\n )\n\nhowever, that is not what the \"not-tags\" expression is for. It should be grabbing only the instances that do NOT have BOTH T1 and T2 tags assigned to them.","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":10618,"name":"Pavel Kholkin","email":"p.v.holkin@mail.ru","username":"pkholkin"},"change_message_id":"ecd4c02c17ad31f8629b99ba003c15b7b525e5f0","unresolved":false,"context_lines":[{"line_number":2043,"context_line":"        tags \u003d filters.pop(\u0027not-tags\u0027)"},{"line_number":2044,"context_line":"        query_prefix \u003d query_prefix.join(models.Instance.tags)"},{"line_number":2045,"context_line":"        query_prefix \u003d query_prefix.filter(~models.Instance.tags.any("},{"line_number":2046,"context_line":"            models.Tag.tag.in_(tags)))"},{"line_number":2047,"context_line":""},{"line_number":2048,"context_line":"    if \u0027not-tags-any\u0027 in filters:"},{"line_number":2049,"context_line":"        tags \u003d filters.pop(\u0027not-tags-any\u0027)"}],"source_content_type":"text/x-python","patch_set":1,"id":"ba8a016a_03f7e08b","line":2046,"in_reply_to":"ba8a016a_aa8fe721","updated":"2015-11-24 08:26:51.000000000","message":"Jay, I think you are wrong. In not-tags we want to get all servers that don\u0027t have t1 AND don\u0027t have t2. \n\nHere De Morgan\u0027s laws are used. We have:\n\n    result \u003d not ((server has t1) or (server has t2)) \u003d (not (server has t1)) and (not (server has t2))","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"de933d0c7bb7e349b5888dd99277d101d7198c8e","unresolved":false,"context_lines":[{"line_number":2056,"context_line":"        for tag in tags:"},{"line_number":2057,"context_line":"            tag_alias \u003d aliased(models.Tag)"},{"line_number":2058,"context_line":"            subq \u003d subq.join(tag_alias, models.Instance.tags)"},{"line_number":2059,"context_line":"            subq \u003d subq.filter(tag_alias.tag \u003d\u003d tag)"},{"line_number":2060,"context_line":""},{"line_number":2061,"context_line":"        query_prefix \u003d query_prefix.filter(~models.Instance.uuid.in_(subq))"},{"line_number":2062,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"ba8a016a_2a03f743","line":2059,"updated":"2015-11-23 17:07:44.000000000","message":"And this is actually implementing the not-tags expression :) The above produces this SQL:\n\n SELECT ... FROM instances i\n WHERE i.uuid NOT IN (\n SELECT u2.uuid FROM instances i2\n JOIN tags t1 ON i2.uuid \u003d t1.resource_id\n AND t1.tag \u003d T1\n JOIN tags t2 ON i.uuid \u003d t2.resource_id\n AND t2.tag \u003d T2);\n\nWhich returns any instance that has neither the T1 nor the T2 tag.","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":10618,"name":"Pavel Kholkin","email":"p.v.holkin@mail.ru","username":"pkholkin"},"change_message_id":"ecd4c02c17ad31f8629b99ba003c15b7b525e5f0","unresolved":false,"context_lines":[{"line_number":2056,"context_line":"        for tag in tags:"},{"line_number":2057,"context_line":"            tag_alias \u003d aliased(models.Tag)"},{"line_number":2058,"context_line":"            subq \u003d subq.join(tag_alias, models.Instance.tags)"},{"line_number":2059,"context_line":"            subq \u003d subq.filter(tag_alias.tag \u003d\u003d tag)"},{"line_number":2060,"context_line":""},{"line_number":2061,"context_line":"        query_prefix \u003d query_prefix.filter(~models.Instance.uuid.in_(subq))"},{"line_number":2062,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"ba8a016a_8366704f","line":2059,"in_reply_to":"ba8a016a_2a03f743","updated":"2015-11-24 08:26:51.000000000","message":"vice versa)\n\n    result \u003d not ((server has t1) and (server has t2)) \u003d (not (server has t1)) or (not (server has t2))","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":9569,"name":"Sergey Nikitin","email":"nikitinserv@gmail.com","username":"snikitin"},"change_message_id":"ef1ec635ac5c4355168e9725a13d45539af776e6","unresolved":false,"context_lines":[{"line_number":2039,"context_line":"        query_prefix \u003d query_prefix.join(tag_alias, models.Instance.tags)"},{"line_number":2040,"context_line":"        query_prefix \u003d query_prefix.filter(tag_alias.tag.in_(tags))"},{"line_number":2041,"context_line":""},{"line_number":2042,"context_line":"    if \u0027not-tags\u0027 in filters:"},{"line_number":2043,"context_line":"        tags \u003d filters.pop(\u0027not-tags\u0027)"},{"line_number":2044,"context_line":"        first_tag \u003d tags.pop(0)"},{"line_number":2045,"context_line":"        subq \u003d query_prefix.session.query(models.Tag.resource_id)"}],"source_content_type":"text/x-python","patch_set":2,"id":"9a8ffd7b_ca404b0a","line":2042,"updated":"2015-12-01 14:11:17.000000000","message":"First of all: \"not-tags\" and \"not-tags-any\" is NOT mutually exclusive. \n\n\"tags\" and \"not-tags\" IS mutually exclusive.\n\n\"tags-any\" and \"not-tags-any\" IS mutually exclusive.","commit_id":"97e02489f00a64dc21c06a8ff6f6eb9d2c36c4e9"},{"author":{"_account_id":11816,"name":"mike bayer","display_name":"Mike Bayer","email":"mike_mp@zzzcomputing.com","username":"zzzeek","status":"Red Hat"},"change_message_id":"1bfc5d1e37e19be79f09ab1698886a6a6241929a","unresolved":false,"context_lines":[{"line_number":2042,"context_line":"    if \u0027not-tags\u0027 in filters:"},{"line_number":2043,"context_line":"        tags \u003d filters.pop(\u0027not-tags\u0027)"},{"line_number":2044,"context_line":"        first_tag \u003d tags.pop(0)"},{"line_number":2045,"context_line":"        subq \u003d query_prefix.session.query(models.Tag.resource_id)"},{"line_number":2046,"context_line":"        subq \u003d subq.join(models.Instance.tags)"},{"line_number":2047,"context_line":"        subq \u003d subq.filter(models.Tag.tag \u003d\u003d first_tag)"},{"line_number":2048,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"9a8ffd7b_53972290","line":2045,"updated":"2015-11-25 15:53:41.000000000","message":"not looking *too* closely but if we are going to make this a subquery and do \"not instance.uuid in (subq of all tags)\",  why do we need the Instance table at all in the subquery?  If Tag.resource_id is the UUID we\u0027re using for the IN, I don\u0027t see what the join(Instance.tags) is used for here, that pulls in the Instance table but this can all be among the Tag table.","commit_id":"97e02489f00a64dc21c06a8ff6f6eb9d2c36c4e9"},{"author":{"_account_id":11816,"name":"mike bayer","display_name":"Mike Bayer","email":"mike_mp@zzzcomputing.com","username":"zzzeek","status":"Red Hat"},"change_message_id":"1bfc5d1e37e19be79f09ab1698886a6a6241929a","unresolved":false,"context_lines":[{"line_number":2048,"context_line":""},{"line_number":2049,"context_line":"        for tag in tags:"},{"line_number":2050,"context_line":"            tag_alias \u003d aliased(models.Tag)"},{"line_number":2051,"context_line":"            subq \u003d subq.join(tag_alias, models.Instance.tags)"},{"line_number":2052,"context_line":"            subq \u003d subq.filter(tag_alias.tag \u003d\u003d tag)"},{"line_number":2053,"context_line":""},{"line_number":2054,"context_line":"        query_prefix \u003d query_prefix.filter(~models.Instance.uuid.in_(subq))"}],"source_content_type":"text/x-python","patch_set":2,"id":"9a8ffd7b_13dc3a11","line":2051,"updated":"2015-11-25 15:53:41.000000000","message":"this is odd, we\u0027re querying against Tag.resource_id but then aliasing out over and over to aliases of Tag, why isn\u0027t the subquery just \"SELECT resource_id FROM tag_table WHERE tag IN (list of not-tags)\" ?   this is the first code I\u0027m looking at this AM so I might be reading this all wrong but all the joining here seems odd.","commit_id":"97e02489f00a64dc21c06a8ff6f6eb9d2c36c4e9"},{"author":{"_account_id":9569,"name":"Sergey Nikitin","email":"nikitinserv@gmail.com","username":"snikitin"},"change_message_id":"ef1ec635ac5c4355168e9725a13d45539af776e6","unresolved":false,"context_lines":[{"line_number":2048,"context_line":""},{"line_number":2049,"context_line":"        for tag in tags:"},{"line_number":2050,"context_line":"            tag_alias \u003d aliased(models.Tag)"},{"line_number":2051,"context_line":"            subq \u003d subq.join(tag_alias, models.Instance.tags)"},{"line_number":2052,"context_line":"            subq \u003d subq.filter(tag_alias.tag \u003d\u003d tag)"},{"line_number":2053,"context_line":""},{"line_number":2054,"context_line":"        query_prefix \u003d query_prefix.filter(~models.Instance.uuid.in_(subq))"}],"source_content_type":"text/x-python","patch_set":2,"id":"9a8ffd7b_f6b89677","line":2051,"in_reply_to":"9a8ffd7b_13dc3a11","updated":"2015-12-01 14:11:17.000000000","message":"I agree that this code looks ugly. I want to make it more pretty but I don\u0027t know how:(\n\nThe problem is that \n\n  SELECT resource_id FROM tag_table WHERE tag IN (list of not-tags)\n\nreturns the list of resouces which have ANY of tags in \"not-tags\" list.\nHere we have two problems:\n\n1. In subquery we need to get UUIDs of instances which have ALL of tags in \"not-tags\".\n\n2. Tags table has only two fields. \u0027tag\u0027 and \u0027resource_id\u0027. Secound field named \u0027resource_id\u0027 because in future we can use this table for storing tags of other resouces. So I worry that if we would NOT join instances table with tags table, we get a correlation problem. For example:\n\nLet\u0027s imagine that we have two different resources (inatnce and some_other_resource) with equal UUID. I know that this is unlikely.\nAlso let\u0027s image that in terms of \"not-tags\" filter this instance is \"good\" and some_other_resource is \"bad\". \n\nIn this situation the subquery will return UUIDs of \"bad\" *resources*. And then we will remove instances with these \"bad\" UUIDs form query response. In this case we will NOT include our \"good\" instance to the responce, because uuid of this instance is  equal uuid of \"bad\" some_other_resource.","commit_id":"97e02489f00a64dc21c06a8ff6f6eb9d2c36c4e9"},{"author":{"_account_id":11816,"name":"mike bayer","display_name":"Mike Bayer","email":"mike_mp@zzzcomputing.com","username":"zzzeek","status":"Red Hat"},"change_message_id":"756cb25a0dc8785145033d6e12f25b26466b7653","unresolved":false,"context_lines":[{"line_number":2048,"context_line":""},{"line_number":2049,"context_line":"        for tag in tags:"},{"line_number":2050,"context_line":"            tag_alias \u003d aliased(models.Tag)"},{"line_number":2051,"context_line":"            subq \u003d subq.join(tag_alias, models.Instance.tags)"},{"line_number":2052,"context_line":"            subq \u003d subq.filter(tag_alias.tag \u003d\u003d tag)"},{"line_number":2053,"context_line":""},{"line_number":2054,"context_line":"        query_prefix \u003d query_prefix.filter(~models.Instance.uuid.in_(subq))"}],"source_content_type":"text/x-python","patch_set":2,"id":"9a8ffd7b_ac7cdeeb","line":2051,"in_reply_to":"9a8ffd7b_f6b89677","updated":"2015-12-02 00:38:32.000000000","message":"Re #1, I wonder why such an operator is even needed, that\u0027s kind of an odd use case.  the JOINs here will be a killer especially that we are joining on a UUID textual column.  you could still join out among \"tags\" aliases themselves on a common set of resource_id values, without joining out to Instance.  Put that in a subquery.  Then join that out to Instance.   Still don\u0027t need many joins from Instance -\u003e Tags.  You want to get a set of \"resource ids that belong to *ALL* these tags\", then filter Instance for \"its resource id is not IN this list of resource ids\".\n\nRe #2, OK, if I\u0027m understanding correctly, this is the \"resource_id might refer to *multiple tables* pattern, e.g. the \"polymorphic association\".  this is **exactly** the kind of pattern I\u0027m super-duper opposed to and I discuss for example here: http://lists.openstack.org/pipermail/openstack-dev/2015-November/080113.html.   It\u0027s a super-wrong, awful, no good pattern and here, just *talking* about it is resulting in us having poorer queries :).\n\nthat said, the preferred pattern would be using integer IDs for foreign keys, which means the UUID wouldn\u0027t even be in the tags table and then you need a join anyway.","commit_id":"97e02489f00a64dc21c06a8ff6f6eb9d2c36c4e9"},{"author":{"_account_id":11816,"name":"mike bayer","display_name":"Mike Bayer","email":"mike_mp@zzzcomputing.com","username":"zzzeek","status":"Red Hat"},"change_message_id":"1bfc5d1e37e19be79f09ab1698886a6a6241929a","unresolved":false,"context_lines":[{"line_number":2056,"context_line":"    if \u0027not-tags-any\u0027 in filters:"},{"line_number":2057,"context_line":"        tags \u003d filters.pop(\u0027not-tags-any\u0027)"},{"line_number":2058,"context_line":""},{"line_number":2059,"context_line":"        query_prefix \u003d query_prefix.outerjoin(models.Instance.tags)"},{"line_number":2060,"context_line":"        query_prefix \u003d query_prefix.filter(~models.Instance.tags.any("},{"line_number":2061,"context_line":"            models.Tag.tag.in_(tags)))"},{"line_number":2062,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"9a8ffd7b_68691525","line":2059,"updated":"2015-11-25 15:53:41.000000000","message":"the ~Instance.tags.any() comes out to \"NOT EXISTS (SELECT * from tags where tags.instance_id\u003dmodel.id\", e.g. a correlated subquery, so you shouldn\u0027t need to join or outerjoin here at all.   Unless this has something to do with ameliorating the effect of a previous filter?   Shouldn\u0027t \"not-tags\" and \"not-tags-any\" be mutually exclusive, is that enforced here?","commit_id":"97e02489f00a64dc21c06a8ff6f6eb9d2c36c4e9"},{"author":{"_account_id":9569,"name":"Sergey Nikitin","email":"nikitinserv@gmail.com","username":"snikitin"},"change_message_id":"ef1ec635ac5c4355168e9725a13d45539af776e6","unresolved":false,"context_lines":[{"line_number":2056,"context_line":"    if \u0027not-tags-any\u0027 in filters:"},{"line_number":2057,"context_line":"        tags \u003d filters.pop(\u0027not-tags-any\u0027)"},{"line_number":2058,"context_line":""},{"line_number":2059,"context_line":"        query_prefix \u003d query_prefix.outerjoin(models.Instance.tags)"},{"line_number":2060,"context_line":"        query_prefix \u003d query_prefix.filter(~models.Instance.tags.any("},{"line_number":2061,"context_line":"            models.Tag.tag.in_(tags)))"},{"line_number":2062,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"9a8ffd7b_815d4617","line":2059,"in_reply_to":"9a8ffd7b_68691525","updated":"2015-12-01 14:11:17.000000000","message":"Hm, I checked that this section works fine without joining.. I\u0027m surprised. And as I understand here we escaped the second problem form the previous comment. So I think I should remove join here :)","commit_id":"97e02489f00a64dc21c06a8ff6f6eb9d2c36c4e9"},{"author":{"_account_id":10618,"name":"Pavel Kholkin","email":"p.v.holkin@mail.ru","username":"pkholkin"},"change_message_id":"459a4617350801b106be16e3d758e021e645cce0","unresolved":false,"context_lines":[{"line_number":1940,"context_line":"    |        \u0027tags\u0027: [some-tag, some-another-tag],"},{"line_number":1941,"context_line":"    |        \u0027tags-any: [some-any-tag, some-another-any-tag],"},{"line_number":1942,"context_line":"    |        \u0027not-tags: [some-not-tag, some-another-not-tag],"},{"line_number":1943,"context_line":"    |        \u0027not-tags-any: [some-not-any-tag, some-another-any-tag]"},{"line_number":1944,"context_line":"    |    }"},{"line_number":1945,"context_line":""},{"line_number":1946,"context_line":"    \"\"\""}],"source_content_type":"text/x-python","patch_set":3,"id":"7a740942_7a6070a5","line":1943,"updated":"2015-12-14 13:30:46.000000000","message":"the second should be \u0027some-another-not-any-tag\u0027","commit_id":"f742092ec6ac00d2ec61c502bb19c3d067a3645a"},{"author":{"_account_id":5441,"name":"Andrew Laski","email":"andrew@lascii.com","username":"alaski"},"change_message_id":"15b73371070a912f2da12a0a2a01dab99850f615","unresolved":false,"context_lines":[{"line_number":2362,"context_line":"        first_tag \u003d tags.pop(0)"},{"line_number":2363,"context_line":"        subq \u003d query_prefix.session.query(models.Tag.resource_id)"},{"line_number":2364,"context_line":"        subq \u003d subq.join(models.Instance.tags)"},{"line_number":2365,"context_line":"        subq \u003d subq.filter(models.Tag.tag \u003d\u003d first_tag)"},{"line_number":2366,"context_line":""},{"line_number":2367,"context_line":"        for tag in tags:"},{"line_number":2368,"context_line":"            tag_alias \u003d aliased(models.Tag)"}],"source_content_type":"text/x-python","patch_set":31,"id":"9a061dce_551d00bc","line":2365,"updated":"2016-04-05 19:30:27.000000000","message":"Why is the first tag treated differently than the remaining tags?","commit_id":"5d430b25166f0c205751223adaa80ec9fe9fbbf1"},{"author":{"_account_id":5441,"name":"Andrew Laski","email":"andrew@lascii.com","username":"alaski"},"change_message_id":"15b73371070a912f2da12a0a2a01dab99850f615","unresolved":false,"context_lines":[{"line_number":2365,"context_line":"        subq \u003d subq.filter(models.Tag.tag \u003d\u003d first_tag)"},{"line_number":2366,"context_line":""},{"line_number":2367,"context_line":"        for tag in tags:"},{"line_number":2368,"context_line":"            tag_alias \u003d aliased(models.Tag)"},{"line_number":2369,"context_line":"            subq \u003d subq.join(tag_alias, models.Instance.tags)"},{"line_number":2370,"context_line":"            subq \u003d subq.filter(tag_alias.tag \u003d\u003d tag)"},{"line_number":2371,"context_line":""}],"source_content_type":"text/x-python","patch_set":31,"id":"9a061dce_9564b849","line":2368,"updated":"2016-04-05 19:30:27.000000000","message":"Can this be moved out of the loop? It\u0027s not related to each tag so it just needs to be done once right?","commit_id":"5d430b25166f0c205751223adaa80ec9fe9fbbf1"},{"author":{"_account_id":9569,"name":"Sergey Nikitin","email":"nikitinserv@gmail.com","username":"snikitin"},"change_message_id":"84752eafaf5dbf1a1f7b014e602d53a3bd74562a","unresolved":false,"context_lines":[{"line_number":2365,"context_line":"        subq \u003d subq.filter(models.Tag.tag \u003d\u003d first_tag)"},{"line_number":2366,"context_line":""},{"line_number":2367,"context_line":"        for tag in tags:"},{"line_number":2368,"context_line":"            tag_alias \u003d aliased(models.Tag)"},{"line_number":2369,"context_line":"            subq \u003d subq.join(tag_alias, models.Instance.tags)"},{"line_number":2370,"context_line":"            subq \u003d subq.filter(tag_alias.tag \u003d\u003d tag)"},{"line_number":2371,"context_line":""}],"source_content_type":"text/x-python","patch_set":31,"id":"9a061dce_6484b35d","line":2368,"in_reply_to":"9a061dce_9564b849","updated":"2016-04-06 12:52:05.000000000","message":"Here we want to join tags table with the previous result and each time we need to use new alias.","commit_id":"5d430b25166f0c205751223adaa80ec9fe9fbbf1"}],"nova/tests/unit/db/test_db_api.py":[{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"a2cd0c3686d274f9a6a89fd28a253c759816b4c4","unresolved":false,"context_lines":[{"line_number":2532,"context_line":""},{"line_number":2533,"context_line":"        result \u003d db.instance_get_all_by_filters(self.ctxt,"},{"line_number":2534,"context_line":"                                                {\u0027not-tags\u0027: [t1, t2]})"},{"line_number":2535,"context_line":"        self._assertEqualListsOfObjects([inst5, inst7], result,"},{"line_number":2536,"context_line":"                ignored_keys\u003d[\u0027deleted\u0027, \u0027deleted_at\u0027, \u0027metadata\u0027, \u0027extra\u0027,"},{"line_number":2537,"context_line":"                              \u0027system_metadata\u0027, \u0027info_cache\u0027, \u0027pci_devices\u0027])"},{"line_number":2538,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"9a8ffd7b_6df3a274","line":2535,"updated":"2015-11-24 17:37:06.000000000","message":"This should be returning [inst1, inst2, inst4, inst5, inst7] because inst3 and inst6 are the only instances that have *BOTH* t1 and t2 tags assigned to them.","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"a2cd0c3686d274f9a6a89fd28a253c759816b4c4","unresolved":false,"context_lines":[{"line_number":2556,"context_line":"        result \u003d db.instance_get_all_by_filters(self.ctxt,"},{"line_number":2557,"context_line":"                                                {\u0027tags\u0027: [t1, t2],"},{"line_number":2558,"context_line":"                                                 \u0027not-tags\u0027: [t4, t5]})"},{"line_number":2559,"context_line":"        self._assertEqualListsOfObjects([inst3], result,"},{"line_number":2560,"context_line":"                ignored_keys\u003d[\u0027deleted\u0027, \u0027deleted_at\u0027, \u0027metadata\u0027, \u0027extra\u0027,"},{"line_number":2561,"context_line":"                              \u0027system_metadata\u0027, \u0027info_cache\u0027, \u0027pci_devices\u0027])"},{"line_number":2562,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"9a8ffd7b_4d181e85","line":2559,"updated":"2015-11-24 17:37:06.000000000","message":"This should be [inst2, inst3] because those are the instances that meet *ALL* of the following conditions:\n\n - The instance must have BOTH of the t1 and t2 tags assigned to it\n - The instance must NOT have BOTH of the t4 and t5 tags assigned to it.","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"a2cd0c3686d274f9a6a89fd28a253c759816b4c4","unresolved":false,"context_lines":[{"line_number":2575,"context_line":""},{"line_number":2576,"context_line":"        result \u003d db.instance_get_all_by_filters(self.ctxt,"},{"line_number":2577,"context_line":"                                                {\u0027tags\u0027: [t1],"},{"line_number":2578,"context_line":"                                                 \u0027not-tags\u0027: [t1]})"},{"line_number":2579,"context_line":"        self.assertEqual([], result)"},{"line_number":2580,"context_line":""},{"line_number":2581,"context_line":"    def test_instance_get_all_by_filters_not_tags_any(self):"}],"source_content_type":"text/x-python","patch_set":1,"id":"9a8ffd7b_207361b3","line":2578,"updated":"2015-11-24 17:37:06.000000000","message":"This is correct. Also contradictory would be the following:\n\n result \u003d db.instance_get_all_by_filters(self.ctxt,\n     {\u0027tags\u0027: [t1],\n       \u0027not-tags-any\u0027: [t1]})\n\nas well, the following would also be contradictory:\n\n result \u003d db.instance_get_all_by_filters(self.ctxt,\n     {\u0027tags-any\u0027: [t1],\n       \u0027not-tags-any\u0027: [t1]})\n\nand this would also be contradictory:\n\n result \u003d db.instance_get_all_by_filters(self.ctxt,\n     {\u0027tags-any\u0027: [t1],\n       \u0027not-tags\u0027: [t1]})","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"a2cd0c3686d274f9a6a89fd28a253c759816b4c4","unresolved":false,"context_lines":[{"line_number":2603,"context_line":"        result \u003d db.instance_get_all_by_filters(self.ctxt,"},{"line_number":2604,"context_line":"                                                {\u0027not-tags-any\u0027: [t1, t2]})"},{"line_number":2605,"context_line":"        self._assertEqualListsOfObjects("},{"line_number":2606,"context_line":"            [inst1, inst2, inst4, inst5, inst7], result,"},{"line_number":2607,"context_line":"            ignored_keys\u003d[\u0027deleted\u0027, \u0027deleted_at\u0027, \u0027metadata\u0027, \u0027extra\u0027,"},{"line_number":2608,"context_line":"                          \u0027system_metadata\u0027, \u0027info_cache\u0027, \u0027pci_devices\u0027])"},{"line_number":2609,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"9a8ffd7b_15822253","line":2606,"updated":"2015-11-24 17:37:06.000000000","message":"this should be returning *ONLY* inst5 and inst7 because they are the only instances that have *neither* the t1 and t2 tags set on them.","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"a2cd0c3686d274f9a6a89fd28a253c759816b4c4","unresolved":false,"context_lines":[{"line_number":2626,"context_line":""},{"line_number":2627,"context_line":"        result \u003d db.instance_get_all_by_filters(self.ctxt,"},{"line_number":2628,"context_line":"                                                {\u0027tags-any\u0027: [t1, t2],"},{"line_number":2629,"context_line":"                                                 \u0027not-tags-any\u0027: [t1, t2]})"},{"line_number":2630,"context_line":"        self._assertEqualListsOfObjects("},{"line_number":2631,"context_line":"            [inst1, inst2, inst4], result,"},{"line_number":2632,"context_line":"            ignored_keys\u003d[\u0027deleted\u0027, \u0027deleted_at\u0027, \u0027metadata\u0027, \u0027extra\u0027,"}],"source_content_type":"text/x-python","patch_set":1,"id":"9a8ffd7b_80e7ad3f","line":2629,"updated":"2015-11-24 17:37:06.000000000","message":"This is a contradictory request. Essentially, the above is requesting:\n\n - The instance is tagged with *EITHER* t1 or t2\n - The instance is NOT tagged with *EITHER* t1 or t2\n\nWhy is this contradictory? Because if an instance is tagged with either t1 or t2, then it cannot also NOT be tagged with either t1 or t2.","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":7,"name":"Jay Pipes","email":"jaypipes@gmail.com","username":"jaypipes"},"change_message_id":"a2cd0c3686d274f9a6a89fd28a253c759816b4c4","unresolved":false,"context_lines":[{"line_number":2668,"context_line":"        self._assertEqualListsOfObjects("},{"line_number":2669,"context_line":"            [inst5, inst6, inst7], result,"},{"line_number":2670,"context_line":"            ignored_keys\u003d[\u0027deleted\u0027, \u0027deleted_at\u0027, \u0027metadata\u0027, \u0027extra\u0027,"},{"line_number":2671,"context_line":"                          \u0027system_metadata\u0027, \u0027info_cache\u0027, \u0027pci_devices\u0027])"},{"line_number":2672,"context_line":""},{"line_number":2673,"context_line":"    def test_instance_get_all_by_host_and_node_no_join(self):"},{"line_number":2674,"context_line":"        instance \u003d self.create_instance_with_args()"}],"source_content_type":"text/x-python","patch_set":1,"id":"9a8ffd7b_402c654a","line":2671,"updated":"2015-11-24 17:37:06.000000000","message":"We need to add scenarios that cover instances that have ZERO tags associated with them, since instances with no tags associated with them should be returned from queries of the form not-tags or not-tags-any.","commit_id":"6374e33e783b860fcbd76852f13a2b53c5cdfd9f"},{"author":{"_account_id":10618,"name":"Pavel Kholkin","email":"p.v.holkin@mail.ru","username":"pkholkin"},"change_message_id":"459a4617350801b106be16e3d758e021e645cce0","unresolved":false,"context_lines":[{"line_number":2584,"context_line":"        db.instance_tag_set(self.ctxt, inst1.uuid, [t1, t2, t4, t5])"},{"line_number":2585,"context_line":"        db.instance_tag_set(self.ctxt, inst2.uuid, [t1, t2, t4])"},{"line_number":2586,"context_line":"        db.instance_tag_set(self.ctxt, inst3.uuid, [t1, t2, t3])"},{"line_number":2587,"context_line":"        db.instance_tag_set(self.ctxt, inst4.uuid, [t1, t3])"},{"line_number":2588,"context_line":""},{"line_number":2589,"context_line":"        result \u003d db.instance_get_all_by_filters(self.ctxt,"},{"line_number":2590,"context_line":"                                                {\u0027tags\u0027: [t1, t2],"}],"source_content_type":"text/x-python","patch_set":3,"id":"7a740942_1acfec56","line":2587,"updated":"2015-12-14 13:30:46.000000000","message":"Please also add here inst5 with no tags","commit_id":"f742092ec6ac00d2ec61c502bb19c3d067a3645a"},{"author":{"_account_id":10618,"name":"Pavel Kholkin","email":"p.v.holkin@mail.ru","username":"pkholkin"},"change_message_id":"459a4617350801b106be16e3d758e021e645cce0","unresolved":false,"context_lines":[{"line_number":2604,"context_line":""},{"line_number":2605,"context_line":"        db.instance_tag_set(self.ctxt, inst1.uuid, [t1])"},{"line_number":2606,"context_line":"        db.instance_tag_set(self.ctxt, inst2.uuid, [t2, t3])"},{"line_number":2607,"context_line":"        db.instance_tag_set(self.ctxt, inst3.uuid, [t1, t2])"},{"line_number":2608,"context_line":""},{"line_number":2609,"context_line":"        result \u003d db.instance_get_all_by_filters(self.ctxt,"},{"line_number":2610,"context_line":"                                                {\u0027tags\u0027: [t1],"}],"source_content_type":"text/x-python","patch_set":3,"id":"7a740942_baf01892","line":2607,"updated":"2015-12-14 13:30:46.000000000","message":"maybe it will be also good to add here inst4 with no tags","commit_id":"f742092ec6ac00d2ec61c502bb19c3d067a3645a"},{"author":{"_account_id":10618,"name":"Pavel Kholkin","email":"p.v.holkin@mail.ru","username":"pkholkin"},"change_message_id":"459a4617350801b106be16e3d758e021e645cce0","unresolved":false,"context_lines":[{"line_number":2638,"context_line":"        db.instance_tag_set(self.ctxt, inst2.uuid, [t2])"},{"line_number":2639,"context_line":"        db.instance_tag_set(self.ctxt, inst3.uuid, [t1, t2])"},{"line_number":2640,"context_line":"        db.instance_tag_set(self.ctxt, inst4.uuid, [t1, t3])"},{"line_number":2641,"context_line":"        db.instance_tag_set(self.ctxt, inst5.uuid, [t1, t2, t3])"},{"line_number":2642,"context_line":""},{"line_number":2643,"context_line":"        result \u003d db.instance_get_all_by_filters(self.ctxt,"},{"line_number":2644,"context_line":"                                                {\u0027tags-any\u0027: [t1, t2],"}],"source_content_type":"text/x-python","patch_set":3,"id":"7a740942_ba47b8ba","line":2641,"updated":"2015-12-14 13:30:46.000000000","message":"the same","commit_id":"f742092ec6ac00d2ec61c502bb19c3d067a3645a"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"e6e27b995f44af00d2abecb5d696802279686e1e","unresolved":false,"context_lines":[{"line_number":2657,"context_line":"                          \u0027system_metadata\u0027, \u0027info_cache\u0027, \u0027pci_devices\u0027])"},{"line_number":2658,"context_line":""},{"line_number":2659,"context_line":"    def test_instance_get_all_by_filters_not_tags_and_not_tags_any(self):"},{"line_number":2660,"context_line":"        inst1 \u003d self.create_instance_with_args()"},{"line_number":2661,"context_line":"        inst2 \u003d self.create_instance_with_args()"},{"line_number":2662,"context_line":"        inst3 \u003d self.create_instance_with_args()"},{"line_number":2663,"context_line":"        inst4 \u003d self.create_instance_with_args()"},{"line_number":2664,"context_line":"        inst5 \u003d self.create_instance_with_args()"},{"line_number":2665,"context_line":"        inst6 \u003d self.create_instance_with_args()"},{"line_number":2666,"context_line":""},{"line_number":2667,"context_line":"        t1 \u003d u\u0027tag1\u0027"},{"line_number":2668,"context_line":"        t2 \u003d u\u0027tag2\u0027"},{"line_number":2669,"context_line":"        t3 \u003d u\u0027tag3\u0027"},{"line_number":2670,"context_line":"        t4 \u003d u\u0027tag4\u0027"},{"line_number":2671,"context_line":"        t5 \u003d u\u0027tag5\u0027"},{"line_number":2672,"context_line":""},{"line_number":2673,"context_line":"        db.instance_tag_set(self.ctxt, inst1.uuid, [t1])"},{"line_number":2674,"context_line":"        db.instance_tag_set(self.ctxt, inst2.uuid, [t2, t5])"},{"line_number":2675,"context_line":"        db.instance_tag_set(self.ctxt, inst3.uuid, [t1, t2])"}],"source_content_type":"text/x-python","patch_set":4,"id":"5a710552_a82b94ea","line":2672,"range":{"start_line":2660,"start_character":0,"end_line":2672,"end_character":0},"updated":"2015-12-15 14:31:10.000000000","message":"There is quite a bit of repetition in the setup part of the added test cases. I accept that the part where you add tags to the instances are needed to be repeated to enhance the readability but the tag creation and instance creation seems unimportant enough to move them to a generic util function.","commit_id":"5089d265ab60248714410e594dbd39d23e0d3036"},{"author":{"_account_id":10618,"name":"Pavel Kholkin","email":"p.v.holkin@mail.ru","username":"pkholkin"},"change_message_id":"ea50253186c223441358d873fd6162429d0d31c8","unresolved":false,"context_lines":[{"line_number":9235,"context_line":"        \u0027metadata\u0027: {\u0027mkey1\u0027: \u0027mval1\u0027, \u0027mkey2\u0027: \u0027mval2\u0027},"},{"line_number":9236,"context_line":"        \u0027system_metadata\u0027: {\u0027smkey1\u0027: \u0027smval1\u0027, \u0027smkey2\u0027: \u0027smval2\u0027},"},{"line_number":9237,"context_line":"        \u0027info_cache\u0027: {\u0027ckey\u0027: \u0027cvalue\u0027}"},{"line_number":9238,"context_line":"    }"},{"line_number":9239,"context_line":""},{"line_number":9240,"context_line":"    def setUp(self):"},{"line_number":9241,"context_line":"        super(TestInstanceTagsFiltering, self).setUp()"}],"source_content_type":"text/x-python","patch_set":6,"id":"5a710552_c65ed611","line":9238,"updated":"2015-12-16 12:57:11.000000000","message":"I think we don\u0027t need so beautiful sample_data, we can use just {}","commit_id":"63955ee54f0777837a812b51fd15e40bde9e806f"}]}
