Complex keys - Map Query

116 views
Skip to first unread message

archit agarwal

unread,
Mar 8, 2014, 4:12:11 AM3/8/14
to mobile-c...@googlegroups.com
Hey Jens!

Getting around the map - reduce to query is kinda giving me a hard time. Any docs you can recommend on this?

In this instance I am trying to narrow down some properties based on the following criteria -

1. location (Can choose multiple)
2. Price (range)
3. move in date (range)

The emit function I have is as follows -

emit (price, location, moveindate) => Is this the best approach or should it be emit(location,price,moveindate)?
How can I possibly send in arrays to reduce this efficiently in the query.startKeys & query.endKeys ?


archit agarwal

unread,
Mar 8, 2014, 4:55:56 AM3/8/14
to mobile-c...@googlegroups.com
I think I got it -

I have to emit (price, location, moveindate) //descending = NO

& then

query.startKey = @[startPrice,@"Marina Bay"];
query.endKey = @[endPrice,@[@"Marina Bay",@"Pasir Ris",@{}],{}]; //In ascending order

Jens Alfke

unread,
Mar 8, 2014, 2:40:57 PM3/8/14
to mobile-c...@googlegroups.com

On Mar 8, 2014, at 1:12 AM, archit agarwal <arch...@gmail.com> wrote:

1. location (Can choose multiple)
2. Price (range)
3. move in date (range)

It’s hard to query on multiple independent ranges like this. The best you can do is to query on one of the ranges, then manually filter the results by the other(s). To decide which criterion to index, consider which one is the best at narrowing in on the desired documents. (The same issue comes up with relational databases, it’s just that SQL will do the in-memory filtering for you, so you don’t have to think about this until you try to figure out why your query is slow.)

What I would do is
emit([location, price], moveInDate)

Then for each given location I’d query by
startKey=[location, minPrice]
endKey=[location, maxPrice]
and then filter the results by comparing the value (the moveInDate) against the date range.

But it’s possible that it would be more efficient to let the view index the moveInDate instead, and manually filter the price range.

—Jens

archit agarwal

unread,
Mar 8, 2014, 8:15:11 PM3/8/14
to mobile-c...@googlegroups.com
Is there something fundamentally wrong with the approach I am currently using i.e. by doing multiple ranges in the keys?
Because it did seem to work fine.

When you say filter manually - you mean just filter it and store it in an array or something? Because I am using CB as a datasource for a tableview.

Jens Alfke

unread,
Mar 8, 2014, 11:27:45 PM3/8/14
to mobile-c...@googlegroups.com

On Mar 8, 2014, at 5:15 PM, archit agarwal <arch...@gmail.com> wrote:

Is there something fundamentally wrong with the approach I am currently using i.e. by doing multiple ranges in the keys?
Because it did seem to work fine. 

The endKey as you gave it in the email doesn’t make sense:
query.startKey = @[startPrice,@"Marina Bay"];
query.endKey = @[endPrice,@[@"Marina Bay",@"Pasir Ris",@{}],{}]; //In ascending order
If your keys are [price, location] then the second element in startKey or endKey has to match against a location. But in the endKey the second element is an array, not a string.

—Jens

archit agarwal

unread,
Mar 11, 2014, 11:25:59 AM3/11/14
to mobile-c...@googlegroups.com

When I use this => It does seem to match against the multiple keys that I have => which are strings and not arrays.
For eg. it matches all with Marina Bay and Pasir Ris

I will check this thoroughly again, but seems to work so far.

Jens Alfke

unread,
Mar 11, 2014, 11:59:52 AM3/11/14
to mobile-c...@googlegroups.com

On Mar 11, 2014, at 8:25 AM, archit agarwal <arch...@gmail.com> wrote:

When I use this => It does seem to match against the multiple keys that I have => which are strings and not arrays. 
For eg. it matches all with Marina Bay and Pasir Ris

That may be because your data set is limited. Collation of arrays isn’t that complicated: the first element is the primary sort key, the second element is the secondary, etc. So if you used
startKey = [300000, “M”]
endKey = [500000, “P”]
it’ll match [300000, “M”], [301000, “A”], [350000, “E”], [499999, “Z”], [500000, “O”], [500000, “P”]. In other words, you’re going to have to manually filter out secondary elements that aren’t in your desired range “M”–“P”.

—Jens

archit agarwal

unread,
Mar 11, 2014, 12:12:14 PM3/11/14
to mobile-c...@googlegroups.com
Got it!
So I have to manually filter it & store it in an array which I can further to use to display in a table?

Jens Alfke

unread,
Mar 11, 2014, 2:11:42 PM3/11/14
to mobile-c...@googlegroups.com

On Mar 11, 2014, at 9:12 AM, archit agarwal <arch...@gmail.com> wrote:

So I have to manually filter it & store it in an array which I can further to use to display in a table?

Basically, yes. You can copy CBLUITableSource.m into your project and modify the -reloadFromQuery method to remove unwanted items from _rows after it assigns to it. (I can add a delegate method to do that, but it’s a bit too late to get it into beta 3.)

—Jens

archit agarwal

unread,
Mar 15, 2014, 12:41:20 PM3/15/14
to mobile-c...@googlegroups.com
Hey Jen, 

Can you give me an example of this method's usage - how can I use this with datasource for this filtering? 

- (void) reloadFromQuery 

Jens Alfke

unread,
Mar 15, 2014, 1:46:58 PM3/15/14
to mobile-c...@googlegroups.com

On Mar 15, 2014, at 9:41 AM, archit agarwal <arch...@gmail.com> wrote:

Can you give me an example of this method's usage - how can I use this with datasource for this filtering? 

- (void) reloadFromQuery 

Modify that method to remove unwanted items from _rows right after it assigns the new value to _rows.

—Jens

archit agarwal

unread,
Mar 16, 2014, 2:01:03 AM3/16/14
to mobile-c...@googlegroups.com
Are you referring to this - 


_datasource.query.rows

archit agarwal

unread,
Mar 16, 2014, 2:07:18 AM3/16/14
to mobile-c...@googlegroups.com
I am not sure when this method should be called..
and how do I access the _rows variable. 

Also with respect to this - 

What I would do is
emit([location, price], moveInDate)

Then for each given location I’d query by
startKey=[location, minPrice]
endKey=[location, maxPrice]
and then filter the results by comparing the value (the moveInDate) against the date range.

when you say for each given location => how would I use a range of locations?
say location - A, C, X, Y, Z

and I want A, X, Z only. 

archit agarwal

unread,
Mar 16, 2014, 2:17:08 AM3/16/14
to mobile-c...@googlegroups.com
Regarding the reloadFromQuery method -- 

Are you saying that I should first subclass CBLUITableSource
and then override the method?

Jens Alfke

unread,
Mar 16, 2014, 2:32:21 AM3/16/14
to mobile-c...@googlegroups.com

On Mar 15, 2014, at 11:17 PM, archit agarwal <arch...@gmail.com> wrote:

> Regarding the reloadFromQuery method --
>
> Are you saying that I should first subclass CBLUITableSource
> and then override the method?

I don’t think subclassing will work, because you need to access instance variables.
I’m saying to copy the .m and .h files into your own project instead of using the ones from the library. Then edit the .m file to do what you want.
CBLUITableSource isn’t really a core part of Couchbase Lite. It’s a utility that does a very common task (running a table view) that you can adapt if you want.

—Jens

Jens Alfke

unread,
Mar 16, 2014, 2:33:51 AM3/16/14
to mobile-c...@googlegroups.com

On Mar 15, 2014, at 11:07 PM, archit agarwal <arch...@gmail.com> wrote:

when you say for each given location => how would I use a range of locations?
say location - A, C, X, Y, Z
and I want A, X, Z only. 

Query once for each location, then merge the results together.

—Jens

archit agarwal

unread,
Mar 16, 2014, 2:52:32 AM3/16/14
to mobile-c...@googlegroups.com
How would I merge the results?
In the same method?

archit agarwal

unread,
Mar 16, 2014, 2:57:37 AM3/16/14
to mobile-c...@googlegroups.com
With respect to CBLUITableSource - 

understood, though I would really prefer not to change that source code...
Would be good if you can put in a delegate method for this. 

archit agarwal

unread,
Mar 16, 2014, 3:55:49 AM3/16/14
to mobile-c...@googlegroups.com

I decided on using a live query and observe it and not use the datasource at all..
This gives me a of flexibility which I wasn't being able to get when I use CBLUITableSource

Do you see any downside to this approach?
Reply all
Reply to author
Forward
0 new messages