YapDatabase icon indicating copy to clipboard operation
YapDatabase copied to clipboard

Is there a way to combine a secondary index with a view?

Open toolboxash opened this issue 10 years ago • 7 comments

If for example we have a collection of posts that have a user_id and I want to display all of the posts by a given user created within the last week?

As far as I understand it I would have to create a secondary index and perform a query as the view controller loads to fetch all of these objects. I would then need to enumerate the transaction retaining all of the objects in an array and then feed these to a table.

The alternative would be to build a filtered view on the fly, however mu understanding here is that this filtered view would then need to iterate over the entire database of posts and deserialise every object in order to evaluate the filter block.

Is there a more optimal way to generate a filtered view using the results of a query on a secondary index?

toolboxash avatar Aug 25 '15 15:08 toolboxash

I believe there has been an attempt to implement a solution for this by extending YapDatabaseSearchResultsView.

https://github.com/ngocluu/YapDatabase

toolboxash avatar Aug 25 '15 18:08 toolboxash

I think Robbie was thinking about building a common foundation as well, and then refactor SearchResultsView to use that

On Tue, Aug 25, 2015 at 11:30 AM, toolboxash [email protected] wrote:

I believe there has been an attempt to implement a solution for this by extending YapDatabaseSearchResultsView.

https://github.com/ngocluu/YapDatabase

— Reply to this email directly or view it on GitHub https://github.com/yapstudios/YapDatabase/issues/206#issuecomment-134694634 .

chrisballinger avatar Aug 25 '15 18:08 chrisballinger

I would really like to see it implemented because driving a table from a query looses all the goodness of views and the nice table view animations which is one of the key benefits of Yap in my view.

toolboxash avatar Aug 25 '15 20:08 toolboxash

Yeah it would be useful for the RTreeIndex as well

On Tue, Aug 25, 2015 at 1:26 PM, toolboxash [email protected] wrote:

I would really like to see it implemented because driving a table from a query looses all the goodness of views and the nice table view animations which is one of the key benefits of Yap in my view.

— Reply to this email directly or view it on GitHub https://github.com/yapstudios/YapDatabase/issues/206#issuecomment-134730003 .

chrisballinger avatar Aug 25 '15 20:08 chrisballinger

A general solution should also consider relationships, in my scenario above I may switch to using a relationship and querying the edge's.

Again it would be nice if the result of that edge query could be fed to a table view a view rather than by enumerating the result into an array.

toolboxash avatar Aug 26 '15 13:08 toolboxash

@toolboxash I believe there is a more optimal way. But first I'd like to quickly address the thoughts from the original post.

As far as I understand it I would have to create a secondary index and perform a query as the view controller loads to fetch all of these objects. I would then need to enumerate the transaction retaining all of the objects in an array and then feed these to a table.

It is true that you would need to perform this query on-the-fly (which is another advantage of Views over Secondary Indexes). However, internally, the results of the query only yield the collection/key of the matching rows. So while you would need to enumerate the results of the transaction, you wouldn't need to retain each object. You could instead simply store the list of collection/key tuples (perhaps using the YapCollectionKey class). This would result in significant decrease in memory (fetch the objects on-the-fly as requested by tableView), and a performance boost too.

The alternative would be to build a filtered view on the fly, however my understanding here is that this filtered view would then need to iterate over the entire database of posts and deserialise every object in order to evaluate the filter block.

This is correct. And this is why a filtered view falls short for your use case.

Is there a more optimal way

We could create a persistent YapDatabaseView to group & sort all posts. We'd group the posts by post.userID. And we'd sort the posts by post.creationDate. Thus, given a User, we can quickly use the view to find all posts by that user, sorted by creation date.

The only problem we have now is filtering the posts to only include those within the last week. And, as you pointed out, a YapDatabaseFilteredView isn't exactly optimal for this task. A secondary index might be ok. But there's a better option.

YapDatabaseViewTransaction contains the following method:

- (NSRange)findRangeInGroup:(NSString *)group using:(YapDatabaseViewFind *)find;

It's an optimized method that allows you to find a range of items within a view's group. Basically, you know that you already have a list of posts that are sorted by date. What you want to know is the range of posts within that group that are greater-than-or-equal-to twoWeeksAgo. Since the posts are already sorted, you could use a binary search algorithm to quickly identify the oldest-yet-valid post. And this is exactly what the find method does.

With the valid range in hand, you could then turn to YapDatabaseMappings.rangeOptionsForGroup. (See [YapDatabaseViewRangeOptions flexibleRangeWithLength:offset:from:]) This allows you to "filter" the existing view automatically using the desired range. :)

The only extra work you have to do concerns updating the mappings+range when the oldest-yet-valid post falls out-of-range. In other words:

  • fetch the oldest-yet-valid post
  • set a timer to fire when it goes out of date
  • invoke findRangeInGroup:using: again
  • update mappings, and tableView accordingly

Let me know if you have any questions.

robbiehanson avatar Sep 09 '15 17:09 robbiehanson

How about with the same situation in hand, I would like to display all the posts by a given user grouped by date? Perhaps I can create a view that groups the posts by [user_id]-[date] and then use the group filter in mappings to pick out those posted by the given user.

tommylitang avatar Sep 24 '15 09:09 tommylitang