-
Notifications
You must be signed in to change notification settings - Fork 264
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
Source filters #2141
Comments
As I read the issue here, I came up with several questions. From what I understood, we are looking for a new Object that can serve as a filter for a source object. It is an object that creates a filter that can be attached to as many sources as we want, but only to sources (as opposed to the BoundingBox filter that is attached to Dataviews), right?. In the original comment, there are only two possible filters mentioned, the category one and the range one, there are some other SQL filters such as |
I have been thinking about this all day long and here's what I came up with. When reading the API reference and per my previous usages of carto.js, I saw that we tend to create instances of various classes to handle all the behaviour of the library, and I think it is better to do it that way rather than using plain old javascript objects. So I followed that path when creating the API of the source filters. To start, I thought about creating a new filter class named So the specification of the methods of the new class could be: carto.filter.SQL
These are the base methods that I thought but we can add as much methods as we want, meanwhile they're useful. The next phase is to be able to add new filters to our filter. (Cpt. Obvious to the rescue 😂) So, I thought that the most useful ones would be a range filter (any filter that has to do with numbers) and a category filter. The names may not be as accurate as possible but we can change them.
The constructor's specification of both filters will be very similar. new carto.filter.SQL.Range('age', { gte: 26, lte: 50 })
new carto.filter.SQL.Category('customerType', {
in: ['Premium', 'Flagship'], not_in: ['Standard']
}); The first parameter will contain the column name, the second will contain the filters that we want to apply to the column rows, and an optional third parameter with options like including null values in the matched set or not. As you can see, there are some filters named By this way, we can define the Filters tree, contained in a carto.filter.SQL instance. The key in here is that every range and category filter will have a The powerful thing about this is that as we have a tree to hold all filters, we can change any of them and it will be updated and we can trigger a reload of the SQL query as if we were changing its content. To make this work with a SQL source, we'll need to add the SQL filter to the source by invoking But I think that examples are worth more than thousand words, so here they are: Basic Example with one range filterconst sqlFilter = new carto.filter.SQL();
const olderThan26 = new carto.filter.SQL.Range('age', { gte: 26 });
sqlFilter.addFilter(olderThan26); Basic Example with one filter and multiple conditionsconst sqlFilter = new carto.filter.SQL();
const between26and50 = new carto.filter.SQL.Range('age', { gte: 26, lte: 50 });
sqlFilter.addFilter(between26and50); Example with two filtersconst sqlFilter = new carto.filter.SQL();
const olderThan26 = new carto.filter.SQL.Range('age', { gte: 26 });
const premiumCustomers = new carto.filter.SQL.Category('customerType', {
in: ['Premium', 'Flagship'], not_in: ['Standard'], like: 'Premium%'
});
sqlFilter.addFilters([olderThan26, premiumCustomers]); Example with subquery category filteringconst sqlFilter = new carto.filter.SQL();
const olderThan26 = new carto.filter.SQL.Range('age', { gte: 26 });
const premiumCustomersSource = new carto.source.SQL("SELECT * FROM customercategories WHERE name IN ('Premium', 'Flagship')");
const premiumCustomers = new carto.filter.SQL.Category('customerType', { in: premiumCustomersSource });
sqlFilter.addFilters([olderThan26, premiumCustomers]); Example with OR filtering with several filtersconst sqlFilter = new carto.filter.SQL();
const olderThan26 = new carto.filter.SQL.Range('age', { gte: 26 });
const premiumCustomersSource = new carto.source.SQL("SELECT * FROM customercategories WHERE name IN ('Premium', 'Flagship')");
const premiumCustomers = new carto.filter.SQL.Category('customerType', { in: premiumCustomersSource });
sqlFilter.addFilter(carto.filter.SQL.OR([olderThan26, premiumCustomers])); Complex example extracted from SalesQuestI'd put these example without splitting into different variables for easiness of writing 😂 const sqlFilter = new carto.filter.SQL();
sqlFilter
.addFilter(new carto.filter.SQL.Category('customerIndustry', { in: ['Banking & Finance', 'Real State'] }))
.addFilter(
new carto.filter.SQL.Range('customerLastContact',
{ lte: new Date("2018-06-24T23:59:59Z"), gte: new Date("2017-01-01T00:00:00Z") },
{ includeNull: true }
)
)
.addFilter(new carto.filter.SQL.Range('customerLastRevenue', { gte: 0 })
.addFilter(new carto.filter.SQL.Category('customerType', { in: ['Premium', 'Flagship'], not_in: ['Standard'] })
.addFilter(new carto.filter.SQL.Category('opportunityType', { in: ['New Business', 'Expansion', "Renewal"] }) Updating filtersconst olderThan26 = new carto.filter.SQL.Range('age', { gte: 26 });
olderThan26.updateAttributes({ in: anotherSQLSource }); Removing filtersconst sqlFilter = new carto.filter.SQL();
const olderThan26 = new carto.filter.SQL.Range('age', { gte: 26 });
sqlFilter.addFilter(olderThan26);
sqlFilter.removeFilter(olderThan26); |
We might need some AND filter to allow more complex situations like this one: sqlFilter.addFilter(
carto.filter.SQL.OR([
carto.filter.SQL.Category(),
carto.filter.SQL.AND([
carto.filter.SQL.Category(),
carto.filter.SQL.Range()
])
])
) We need to define which operators we want to allow in the filters, but I'd add all the ones that might be compelling, like equal, not equal. We can add as many operators as we want. I updated the previous comment so it's better to read it in GitHub rather than in the mail :) |
Very good proposal. 👍 First thoughts:
|
new carto.filter.SQL.Category('customerType', { in: "SELECT * FROM customercategories WHERE name IN ('Premium', 'Flagship')"});
Let me update the examples and I'll post them here as soon as I finish them. I have another concern related to filters. I want to add comparison filters for numbers, like |
Well, after giving a second round of thought, we decided to remove So, all the new shiny examples are: Basic Example with one range filterconst sqlSource = new carto.source.SQL('SELECT * FROM fake_table');
const olderThan26 = new carto.filter.SQL.Range('age', { gte: 26 });
sqlSource.addFilter(olderThan26); Basic Example with one filter and multiple conditionsconst sqlSource = new carto.source.SQL('SELECT * FROM fake_table');
const between26and50 = new carto.filter.SQL.Range('age', { gte: 26, lte: 50 });
sqlSource.addFilter(between26and50); Example with two filtersconst sqlSource = new carto.source.SQL('SELECT * FROM fake_table');
const olderThan26 = new carto.filter.SQL.Range('age', { gte: 26 });
const premiumCustomers = new carto.filter.SQL.Category('customerType', {
in: ['Premium', 'Flagship'], not_in: ['Standard'], like: 'Premium%'
});
sqlSource.addFilters([olderThan26, premiumCustomers]); Example with OR filtering containing several filtersconst sqlSource = new carto.source.SQL('SELECT * FROM fake_table');
const olderThan26 = new carto.filter.SQL.Range('age', { gte: 26 });
const premiumCustomers = new carto.filter.SQL.Category('customerType', { in: ['Premium', 'Flagship'] });
sqlSource.addFilter(carto.filter.SQL.OR([olderThan26, premiumCustomers])); Complex example extracted from SalesQuestI'd put this example without splitting into different variables for easiness of writing 😂 const sqlSource = new carto.source.SQL('SELECT * FROM fake_table');
sqlSource
.addFilter(new carto.filter.SQL.Category('customerIndustry', { in: ['Banking & Finance', 'Real State'] }))
.addFilter(
new carto.filter.SQL.Range('customerLastContact',
{ lte: new Date("2018-06-24T23:59:59Z"), gte: new Date("2017-01-01T00:00:00Z") },
{ includeNull: true }
)
)
.addFilter(new carto.filter.SQL.Range('customerLastRevenue', { gte: 0 })
.addFilter(new carto.filter.SQL.Category('customerType', { not_in: ['Standard'] })
.addFilter(new carto.filter.SQL.Category('opportunityType', { in: ['New Business', 'Expansion', "Renewal"] }) Updating all filter propertiesconst ageFilter = new carto.filter.SQL.Range('age', { gte: 26 });
ageFilter.setOptions({ lte: 26, gte: 35 }); Updating single filter property while maintaining other propertiesconst ageFilter = new carto.filter.SQL.Range('age', { gte: 26 });
olderThan26.set('lte', 26);
olderThan26.set('gte', 35); Removing filtersconst sqlSource = new carto.source.SQL('SELECT * FROM fake_table');
const olderThan26 = new carto.filter.SQL.Range('age', { gte: 26 });
sqlSource.addFilter(olderThan26);
sqlSource.removeFilter(olderThan26); |
@jesusbotella chiming in summoned by @ivanmalagon 😄 examples look OK to me but I have a couple comments from the SalesQuest example:
Sorry if this looks a bit obvious but apart from that, this extension is very likely going to make developers work with widgets way simpler! \o/ |
Nice job! ✨ I have a little proposal about namespaces and naming: carto.filter.SQL. SQL here could be a bit confusing for new users because you think in const olderThan26 = new carto.filter.Range('age', { gte: 26 });
const premiumCustomers = new carto.filter.Category('customerType', { in: ['Premium', 'Flagship'] }); The issue here is that there are other filters, like dataview.addFilter(new carto.filter.Range(...)) -> CartoError
source.addFilter(new carto.filter.BoundingBox(...)) -> CartoError |
Nice! |
Thank you very very much for your feedback! @jsanz Yes, you are totally right. I messed it up with that example 😂. Yes, that filters are mutually exclusive so it makes no sense. I'll modify the example right away. It was just to show any other example but it makes no sense in reality. @Jesus89 You're right. The thing is that I didn't realize that the filters can work with Dataset sources as well 😂, that's why I included the SQL namespace. I thought about throwing a @oriolbx ✨ |
Thank you all for your feedback. +1 to the @Jesus89 suggestion. Bandera! 🇯🇲 We're good to start the implementation |
Thank you! 💪 |
@jesusbotella then I understand the Related with that, apart from the |
@jsanz Yes! I thought so at least 😃. I didn't think about that, but might be interesting as well. But as it would be difficult to implement as an operator like AND or OR, what do you think about adding that as an option like |
as an option for the filter looks good to me as well :-)
…On Tue, Jun 26, 2018 at 6:28 PM Jesús Botella ***@***.***> wrote:
@jsanz <https://github.com/jsanz> Yes! I thought so at least 😃.
I didn't think about that, but might be interesting as well. But as it
would be difficult to implement as an operator like AND or OR, what do you
think about adding that as an option like includeNull?. It would be
something like reverseConditions.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2141 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AALfaGxmKtxxJANz_SI4OrbCU_ksPNdcks5uAmErgaJpZM4UniDL>
.
--
Jorge Sanz
Solutions Engineer & Support Manager
CARTO - Turn Location Data Into Business Outcomes
|
This looks totally awesome, everyone. VAMOS! |
Available in version |
Context
After seeing the customers using CARTO.js v4 in the wild, we've realized that they struggle when it comes to filtering sources. Right now, they have to add helper functions to translate their widgets filters to a source node. This doesn't scalate.
We need to add another object to ease source nodes filtering:
SELECT * FROM wadus WHERE hood IN ['brooklyn', 'queens]
.SELECT * FROM wadus WHERE price >= 34 AND price <= 100
Source nodes should be allowed to be filtered by several filters. The way to do it and what do we allow (ANDs, ORs) is to be discussed in the task of API definition.
Tasks
### Remarks
The text was updated successfully, but these errors were encountered: