Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Could you please provide a couple of examples of using the module? #4

Open
minicuper opened this issue Dec 5, 2015 · 6 comments
Open

Comments

@minicuper
Copy link

Just trying to get all pros and cons.

@cjblomqvist
Copy link
Member

First of all, the main scenarios we've found are scenarios which are typical relational scenarios. In particular, there's no good way to create fully reactive structures of data, which are not the same structure within your DB (e.g. your Mongo collections).

Let's assume a simple system for planning production. You are a producer of ham. You have a bunch of different groups of products, e.g. chicken ham, pork ham, cow ham. Within these groups of products, you have different products, such as "Chicken ham extra spicy", "Chicken ham regular", "Chicken ham Christmas", "Pork ham regular", and so on... You want to keep a pretty high level of normalization within your DB (even though you have a document DB this may be quite beneficial). Thus, you have one collection products and one for productGroups. Each product has one productGroup field with an id, which you always maintain as an id of a productGroup.

When you plan your production, you'd like to keep one field for the inventory status of each product for each day. You'd also like to maintain a field for the amount you'll produce for each product for a specific day, and also how much are to be delivered.

Let's go through some scenarios:

More efficient than filters
The current implementation of Racer's filter-functionality loops through each item in the original list for every event change. This makes for a quite inefficient algorithm to keep the filtered list updated. In derby-view, each item is evaluated for every change, but only on that item. This makes for a vastly more efficient algorithm. An example for above scenario - let's assume we'd like to render a list of all articles belonging to a specific group. Then one could do something like:

// Assuming we've fetched/subscribed all articles earlier in the page rendering process, e.g. in the route most likely. 

// In our init fn:
var myArticleGroup = model.get('articleGroups.<id>'); // The article group we want to maintain a list for
var $articles = model.scope('articles');

model.fn('articles', function (emit, article) {
  emit(article.articleGroup + '|' + article.id);
});

var view = $articles.view('articles');
var query = view.query(myArticleGroup.id, myArticleGroup + 'z');
query.ref('_page.listOfArticlesForGroup'); // Ref to path, similar to filters

The syntax is not necessarily as simple as filter for this specific purpose, but it is:
a) more efficient
b) opens up for much more potential

Let's assume another example. We'd like to create a table where we list the inventory status of each article. We have one column for each day, and one row for each article. This is quite hard today to render with Derby. With derby-view, we can easily do something like:

// Init init fn:
var $stocks = model.scope('stocks');

model.fn('stocksTable', function (emit, stock) {
  emit(stock.date + '.' + stock.article'); // Assuming each stock item has a date and an article property
});

var view = $stocks.view('stocksTable');
view.ref('_page.stocks');
<!-- In our template -->
{{each dates as #date}}
  {{each articles as #article}}
    <div>{{_page.stocks[#date][#article.id]}}</div>
  {{/each}}
{{/each}}

Since derby-view creates references for everything, we can easily make each stock editable;

<!-- In our template -->
{{each dates as #date}}
  {{each articles as #article}}
    <input value="{{_page.stocks[#date][#article.id]}}" />
  {{/each}}
{{/each}}

Let's also assume we'd like to maintain a dynamic sum of all stocks for a specific day. Then we can easily rework it into:

// Init init fn:
var $stocks = model.scope('stocks');

model.fn('stocksTable', function (emit, stock) {
  emit(stock.date + '.' + stock.article'); // Assuming each stock item has a date and an article property
});

var view = $stocks.view('stocksTable');
view.ref('_page.stocks');

model.scope('_page.stocks').on('all', '**', function (path) {
  var segments = path.split('.');
  var date = segments[0];

  var stocks = model.get('_page.stocks.' + date);

  // Loop through and summarize amount for each stock for this date
  var stockSumForDate = ...

  model.scope('_page').set('stocksSums.' + date, stockSumForDate);
})

Then we can extend our view to:

<!-- In our template -->
{{each dates as #date}}
  {{each articles as #article}}
    <div>{{_page.stocks[#date][#article.id]}}</div>
  {{/each}}
  <div class="sum">{{_page.stocksSums[#date]}}</div>
{{/each}}

(in the future, it'd be nice if this could be done automatically using reduce fns, but haven't gotten there yet).

We could also do it without the listener, by using a view helper such as;

<!-- In our template -->
{{each dates as #date}}
  {{each articles as #article}}
    <div>{{_page.stocks[#date][#article.id]}}</div>
  {{/each}}
  <div class="sum">{{ourViewHelper(_page.stocks[#date])}}</div>
{{/each}}

But I've found the viewhelpers to be somewhat unreliable with to many things changing in brackets, so we've mostly had to go with the event listeners and doing it separately.

Another cool thing here is that since we now have all our stocks structured according to date and article, we can do other types of queries, and similar, on that structure. And it will all be reactive, in accordance with the Derby way (derby-view creates ref's for everything).

There are probably a lot of other scenarios which we've yet to discover, but so far we've found the ones above to be most common and useful. Basically, you need to have some more complex views (e.g. tables) in order to realize any big gains from derby-view (otherwise, model.filter works fine for most use cases), and of course you could setup all this yourself (you'd probably want to skip the reactivity then). To set it up yourself have been quite tedious and error/bug prone though, we've found out and with a lot of boilerplate code with a lot of event listeners flying around. In essence we've found derby-view can help a lot when dealing with relational data, and connect various collections to each other.

If you have any scenario's you'd like to bounce ideas about for using derby-view, I'll be more than open for that!

Feel free to catch me on gitter, preferably day time CET week days if you'd like to ask me any questions or thoughts about the library. I'm very excited to hear your feedback, even it is "this is useless crap" ;)

@cjblomqvist
Copy link
Member

Eventually, I might create some real examples within the repo. But for now, I'll keep it like this, as a comment in an issue. I'm a little bit too busy at the moment to deal with that I'm afraid.

@minicuper
Copy link
Author

It really makes sense for me. I played with this a little bit - looks good. By the way, what is the syntax of view.query? Haven't got the point of the line:

var query = view.query(myArticleGroup.id, myArticleGroup + 'z');

What is start and end?

@cjblomqvist
Copy link
Member

All emitted keys are sorted alphabetically in a list, and a query is basically a delimited part of this list, delimited by a start phrase and an end phrase. So, start is a phrase which will converted into an index (a search is made to find the corresponding index of the full list, such as if it would be inserted into the ordered list of keys). The end is the upper delimiter.

Did it become clearer?

@cjblomqvist
Copy link
Member

By the way, the purpose of this is that now we can have things:
a) filtered (through not emitting keys for certain items or by querying for a specific delimited part)
b) sorted, by having all keys automatically sorted
c) efficient, the sorted keys makes for efficient inserts but especially searches (tree lookup) and also only updating the result of a view for an item if that specific item has changed/been updated in any way (and also only do the minimum necessary changes). The only thing not so efficient about the lib currently is the queries, but they're not terrible either and could probably quite easily be fixed too.

@cjblomqvist
Copy link
Member

Oh, and of course
d) a much more convenient data structure to work with
And also
e) possibly linking different collections, since you can emit a path as a second argument passed to the end function, and that can be a path to any item in any collection (possibly any path really, but haven't really tested or thought through the implications of things - in the end I'd like to be able to also be able to emit other stuff like certain values too, but that requires additional work)

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

No branches or pull requests

2 participants