{"action":"created","issue":{"url":"https://api.github.com/repos/vuex-orm/vuex-orm/issues/163","repository_url":"https://api.github.com/repos/vuex-orm/vuex-orm","labels_url":"https://api.github.com/repos/vuex-orm/vuex-orm/issues/163/labels{/name}","comments_url":"https://api.github.com/repos/vuex-orm/vuex-orm/issues/163/comments","events_url":"https://api.github.com/repos/vuex-orm/vuex-orm/issues/163/events","html_url":"https://github.com/vuex-orm/vuex-orm/issues/163","id":325800350,"node_id":"MDU6SXNzdWUzMjU4MDAzNTA=","number":163,"title":"Relations refacoring ideas","user":{"login":"Xcelled","id":5599341,"node_id":"MDQ6VXNlcjU1OTkzNDE=","avatar_url":"https://avatars2.githubusercontent.com/u/5599341?v=4","gravatar_id":"","url":"https://api.github.com/users/Xcelled","html_url":"https://github.com/Xcelled","followers_url":"https://api.github.com/users/Xcelled/followers","following_url":"https://api.github.com/users/Xcelled/following{/other_user}","gists_url":"https://api.github.com/users/Xcelled/gists{/gist_id}","starred_url":"https://api.github.com/users/Xcelled/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Xcelled/subscriptions","organizations_url":"https://api.github.com/users/Xcelled/orgs","repos_url":"https://api.github.com/users/Xcelled/repos","events_url":"https://api.github.com/users/Xcelled/events{/privacy}","received_events_url":"https://api.github.com/users/Xcelled/received_events","type":"User","site_admin":false},"labels":[{"id":806589648,"node_id":"MDU6TGFiZWw4MDY1ODk2NDg=","url":"https://api.github.com/repos/vuex-orm/vuex-orm/labels/discussion","name":"discussion","color":"0e8a16","default":false},{"id":628245172,"node_id":"MDU6TGFiZWw2MjgyNDUxNzI=","url":"https://api.github.com/repos/vuex-orm/vuex-orm/labels/help%20wanted","name":"help wanted","color":"128A0C","default":true}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":12,"created_at":"2018-05-23T17:19:38Z","updated_at":"2018-06-28T02:45:40Z","closed_at":null,"author_association":"NONE","body":"Continuing from #162:\r\n\r\n>Wow thank you so much for your advice! This is super helpful! I do think having indexes for the FK records are interesting and will boost the performance when retrieving data from the state, though I'm not so sure how that would impact the overall user experience.\r\n\r\nHow do you mean?\r\n\r\n> I'm not sure how this would work. Because the database Vuex ORM is using is Vuex Store State, not RDB. Will sql.js and knex run on browser against JSON object as a database...?\r\n\r\nI think you misunderstood what I meant. I'm saying that vuex-orm probably shouldn't go overboard in the types of queries that it does - because relational databases could do them much better anyway. So if a user needed that level of complexity, they'd be better off going with sql.js or something else that's a \"true\" relational database. Vuex is for reactive state information, not emulating a full blown database, and I think it's helpful to bear that in mind so we don't feature-creep too much.\r\n\r\n> This will indeed could be nice when loading data, but at least on my usage, I tend to refresh the Store State a lot. Maybe I do so more than I retrieve data from the state. In this case, maybe it would cost more to create indexes than lookup data each time? Also we have to make sure we delete those index when deleting record or update them when updating the record.\r\n\r\nThis is a valid point. A possibility is to support both and let the user opt-in to indexing. Vuex-orm could then use the index if it exists or fall back to a linear record scan if it doesn't. That said, my suspicion is that creating and maintaining the indexes should be fairly cheap in terms of cost. Hash maps are constant-time-access, so if FK targets are guaranteed to be unique (a safe assumption), then hash maps can be used and the index maintenance cost becomes essentially inconsequential.\r\n\r\n> The second reason is, I think the data stored at Vuex ORM is quite small at most of the time (it was completely wrong with your case though :p). Because even if you have like thousands of record stored at your state, you could only show few in a single page.\r\n\r\nThis doesn't impact the need for indexing. In fact, in that huge-app-dataset (:P) I am showing only a few items at a time on any one page - but the cost-savings of looking up those few items is massive.\r\n\r\n>This is interesting. I'm actually thinking to store such \"prototyped\" data into the Vuex State directly too. In fact, at very beginning of Vuex ORM, I was saving actual class instance to the Vuex State.\r\n\r\n>The reason I stoped doing that is was because Vue official document tells to store only \"plain object\" to the store. I'm still not sure what exactly \"plain\" means, but I did asked at Vue's chat and the core member told me that there might be chance it wouldn't work in future when they implement Proxy based reactivity (Vue 3).\r\n\r\n>But as you know, it works and I think it would work in future too. If we could do that, then yes we can eliminate the need of instantiating object each time we retrieve the data!\r\n\r\nI won't claim to know the ins and outs of Vue's future plans, but I bet there's a workaround for that too. Vue class-based components use properties for computed methods, so that'll have to be addressed anyway."},"comment":{"url":"https://api.github.com/repos/vuex-orm/vuex-orm/issues/comments/400893526","html_url":"https://github.com/vuex-orm/vuex-orm/issues/163#issuecomment-400893526","issue_url":"https://api.github.com/repos/vuex-orm/vuex-orm/issues/163","id":400893526,"node_id":"MDEyOklzc3VlQ29tbWVudDQwMDg5MzUyNg==","user":{"login":"Xcelled","id":5599341,"node_id":"MDQ6VXNlcjU1OTkzNDE=","avatar_url":"https://avatars2.githubusercontent.com/u/5599341?v=4","gravatar_id":"","url":"https://api.github.com/users/Xcelled","html_url":"https://github.com/Xcelled","followers_url":"https://api.github.com/users/Xcelled/followers","following_url":"https://api.github.com/users/Xcelled/following{/other_user}","gists_url":"https://api.github.com/users/Xcelled/gists{/gist_id}","starred_url":"https://api.github.com/users/Xcelled/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Xcelled/subscriptions","organizations_url":"https://api.github.com/users/Xcelled/orgs","repos_url":"https://api.github.com/users/Xcelled/repos","events_url":"https://api.github.com/users/Xcelled/events{/privacy}","received_events_url":"https://api.github.com/users/Xcelled/received_events","type":"User","site_admin":false},"created_at":"2018-06-28T02:45:40Z","updated_at":"2018-06-28T02:45:40Z","author_association":"NONE","body":"This all sounds good, with one comment and two suggestions.\r\n\r\nComment is that indexing isn't the same as caching and it'd produce weird behavior to use an index instead of a cache. For example, if you cache on access (with memoize or similar), you'd end up with the following psudocode which would be a great source of head-banging bugs:\r\n\r\n```js\r\nentities.store({ id: 1, foo: 'bar'});\r\nvar x = entities.get(1);\r\nconsole.log(x.foo); // bar, as expected\r\nentities.store({ id: 1, foo: 'something else'}); // Overwrite data\r\nconsole.log(entities.get(1).foo); // Still prints 'bar' since it's returning the cached object!\r\n```\r\n\r\nTo fix this, you'd need to evict the old entity from the cache when storing a new one, and at that point you've recreated a (less efficient) index :P\r\n\r\nMy first suggestion is to make reverse indexing explicit (ie, `Bar.HasOne('foo').WithReverse('bars')`. That way, you can make two different indexes (one on Foos, and one on Bars) and treat both as \"forward\" indexes. The idea is also to build up a type of relation map at startup, so you know at the outset which types have which relations and indexes.\r\n\r\nMy second suggestion is to simplify how you're creating the indexes. If you take the view that indexes only need to be updated when storing a record or deleting one, the whole indexing problem (to me) simplifies down to a \"put\" and \"delete\" method duo. Instead of trying to figure out what indexes to add where and when and all that, just run *every* entity operation through the put and delete methods.\r\n\r\nThese two methods can take care of scanning the record for relations, converting the relations into the appropriate property type (index or full scan), adding the record to the appropriate indexes, and finally putting the entity into the store. Combined with my first suggestion, each type will know exactly which indexes it needs to update. Delete will remove from indexes and the store.\r\n\r\nSo Normalizr will give you (essentially) an array of entities - just shove these through the put method and your indexes are all up to date!\r\n\r\nImo, you could say that updates direct updates to an indexed property are not supported (which they shouldn't be, following vuex conventions) and instead say an \"update\" is actually a delete of the old record followed by a store of the new one. Since this uses the methods already defined, indexing is consistent by default.\r\n\r\nMutating the store directly (ie, without put/delete) is undefined behavior."}}
{"actor":{"display_login":"Xcelled"}}