{"action":"created","issue":{"url":"https://api.github.com/repos/angular/angular.js/issues/11752","labels_url":"https://api.github.com/repos/angular/angular.js/issues/11752/labels{/name}","comments_url":"https://api.github.com/repos/angular/angular.js/issues/11752/comments","events_url":"https://api.github.com/repos/angular/angular.js/issues/11752/events","html_url":"https://github.com/angular/angular.js/issues/11752","id":71625870,"number":11752,"title":"Regression in filterFilter 1.3.6 changes when using a custom comparator function","user":{"login":"dcartwright","id":5149945,"avatar_url":"https://avatars.githubusercontent.com/u/5149945?v=3","gravatar_id":"","url":"https://api.github.com/users/dcartwright","html_url":"https://github.com/dcartwright","followers_url":"https://api.github.com/users/dcartwright/followers","following_url":"https://api.github.com/users/dcartwright/following{/other_user}","gists_url":"https://api.github.com/users/dcartwright/gists{/gist_id}","starred_url":"https://api.github.com/users/dcartwright/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcartwright/subscriptions","organizations_url":"https://api.github.com/users/dcartwright/orgs","repos_url":"https://api.github.com/users/dcartwright/repos","events_url":"https://api.github.com/users/dcartwright/events{/privacy}","received_events_url":"https://api.github.com/users/dcartwright/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/angular/angular.js/labels/component:%20filters","name":"component: filters","color":"bfdadc"},{"url":"https://api.github.com/repos/angular/angular.js/labels/severity:%20regression","name":"severity: regression","color":"e11d21"},{"url":"https://api.github.com/repos/angular/angular.js/labels/type:%20bug","name":"type: bug","color":"000000"}],"state":"open","locked":false,"assignee":{"login":"gkalpak","id":8604205,"avatar_url":"https://avatars.githubusercontent.com/u/8604205?v=3","gravatar_id":"","url":"https://api.github.com/users/gkalpak","html_url":"https://github.com/gkalpak","followers_url":"https://api.github.com/users/gkalpak/followers","following_url":"https://api.github.com/users/gkalpak/following{/other_user}","gists_url":"https://api.github.com/users/gkalpak/gists{/gist_id}","starred_url":"https://api.github.com/users/gkalpak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gkalpak/subscriptions","organizations_url":"https://api.github.com/users/gkalpak/orgs","repos_url":"https://api.github.com/users/gkalpak/repos","events_url":"https://api.github.com/users/gkalpak/events{/privacy}","received_events_url":"https://api.github.com/users/gkalpak/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/angular/angular.js/milestones/35","html_url":"https://github.com/angular/angular.js/milestones/Purgatory","labels_url":"https://api.github.com/repos/angular/angular.js/milestones/35/labels","id":526241,"number":35,"title":"Purgatory","description":"This milestone is for issues and PRs that weren't accepted yet and we are waiting for more info from the submitter.\r\n\r\nFor issues this typically means that we weren't able to reproduce the issue or we haven't decided yet if a feature request is a good idea at all.\r\n\r\nFor PRs this means that the PR still needs a lot of work and we might not even be interested in merging it because it looks like a non-core feature. We are still waiting for more discussion within the team or more info from the contributor before making the final decision.","creator":{"login":"IgorMinar","id":216296,"avatar_url":"https://avatars.githubusercontent.com/u/216296?v=3","gravatar_id":"","url":"https://api.github.com/users/IgorMinar","html_url":"https://github.com/IgorMinar","followers_url":"https://api.github.com/users/IgorMinar/followers","following_url":"https://api.github.com/users/IgorMinar/following{/other_user}","gists_url":"https://api.github.com/users/IgorMinar/gists{/gist_id}","starred_url":"https://api.github.com/users/IgorMinar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/IgorMinar/subscriptions","organizations_url":"https://api.github.com/users/IgorMinar/orgs","repos_url":"https://api.github.com/users/IgorMinar/repos","events_url":"https://api.github.com/users/IgorMinar/events{/privacy}","received_events_url":"https://api.github.com/users/IgorMinar/received_events","type":"User","site_admin":false},"open_issues":109,"closed_issues":338,"state":"open","created_at":"2014-01-04T01:20:50Z","updated_at":"2015-04-29T13:33:10Z","due_on":null,"closed_at":null},"comments":5,"created_at":"2015-04-28T15:43:55Z","updated_at":"2015-04-29T17:53:15Z","closed_at":null,"body":"When I upgraded from an earlier version of 1.3.x (1.3.0) to the latest version (1.3.15), several uses of filterFilter were broken. I traced the regression back to the changes that were implemented in 1.3.6 (specifically I think it was https://github.com/angular/angular.js/commit/f7cf846045b1e2fb39c62e304c61b44d5c805e31). I believe this same issue probably effects 1.4.x as well, although I have not tested it.\r\n\r\nIn my use case, I have a filter that looks like this:\r\n`ng-repeat=\"... | filter:{Slot: slot}:modelEquals\"`\r\n\r\nWhat's happening here is that `Slot`, the property, is a reference to a model object on the array elements. This then gets compared to `slot`, the iterator variable, from a wrapping ng-repeat. The comparison function is `modelEquals`, which does a comparison based on the object prototype and ID property. The specifics of the comparison function are not relevant because my problem is that the comparison function is never called!\r\n\r\nHere are two jsfiddles that reproduce the problem with as little complexity as I could manage and still reproduce the problem:\r\n\r\n1.3.6 - This shows the problem. You can see that my custom comparison function is never called.\r\nhttp://jsfiddle.net/thcjthcy/17/\r\n\r\n1.3.5 - This is the exact same functional code (just different explanatory text), but referencing 1.3.5. Notice that my comparison function is called as expected.\r\nhttp://jsfiddle.net/thcjthcy/16/\r\n\r\nI debugged into the 1.3.6+ code a bit, and I think the problem comes when the `deepCompare` function recurses whenever the expected value is an object (https://github.com/angular/angular.js/blob/v1.3.15/src/ng/filter/filter.js#L216). I believe I understand why it does this (wants to support comparisons of nested properties), however in my case the expected value is a  complex object, and the comparison seems to get lost in the woods, so to speak, and my comparison function is never called. In the 1.3.5 code, since I don't believe it recurses, my comparison function is called with the top-level expected and actual object values.\r\n\r\nI believe this is a bug because it doesn't match the documentation, or at least my understanding of it. Specifically:\r\n\r\nhttps://github.com/angular/angular.js/blob/v1.3.15/src/ng/filter/filter.js#L32\r\n`Note that a named property will match properties on the same level only, while the special $ property will match properties on the same level or deeper`\r\nI am using a named property, and yet the code is recursing to a deeper level instead of calling my comparison function on the top-level property.\r\n\r\nFor the time being, my work-around is to stay on 1.3.5. If this is just how filterFilter is supposed to work now, and there won't be an \"opposite of $\" kind of marker that lets me short-circuit the deep comparison, I would probably work around this by writing a filter function which takes the property name and expected value and does the key indexing into the array item (actual) manually. This isn't a huge amount of work, but I wanted to see if the built-in filterFilter was supposed to be able to support this use case before I ditched it."},"comment":{"url":"https://api.github.com/repos/angular/angular.js/issues/comments/97518015","html_url":"https://github.com/angular/angular.js/issues/11752#issuecomment-97518015","issue_url":"https://api.github.com/repos/angular/angular.js/issues/11752","id":97518015,"user":{"login":"dcartwright","id":5149945,"avatar_url":"https://avatars.githubusercontent.com/u/5149945?v=3","gravatar_id":"","url":"https://api.github.com/users/dcartwright","html_url":"https://github.com/dcartwright","followers_url":"https://api.github.com/users/dcartwright/followers","following_url":"https://api.github.com/users/dcartwright/following{/other_user}","gists_url":"https://api.github.com/users/dcartwright/gists{/gist_id}","starred_url":"https://api.github.com/users/dcartwright/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dcartwright/subscriptions","organizations_url":"https://api.github.com/users/dcartwright/orgs","repos_url":"https://api.github.com/users/dcartwright/repos","events_url":"https://api.github.com/users/dcartwright/events{/privacy}","received_events_url":"https://api.github.com/users/dcartwright/received_events","type":"User","site_admin":false},"created_at":"2015-04-29T17:53:15Z","updated_at":"2015-04-29T17:53:15Z","body":"@gkalpak I think maybe we just aren't seeing eye-to-eye on the correct behavior of the filter expression. To me, it's a bit weird/unexpected for a named property to recurse to sub-objects. I don't need or want deep matching, especially if it breaks shallow matching, and it seems like the documentation (at least the part I quoted above) suggests that deep matching is only employed with the special \"$\" key. I guess I'm a bit confused about what deep matching even means - it looks through the entire object graph of my iterated objects looking for any property named \"foo\"? Does it only look at foo.foo.foo..., or does it look for a property named \"foo\" anywhere in the entire object graph? Isn't the latter hopelessly inefficient in the general case where we just need basic shallow matching?"}}