Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[core] Introduce dedicated filter types for $type and $id special cas…
Browse files Browse the repository at this point in the history
…es (#7971)

* [core] Introduce dedicated filter types for $type and $id special cases

* [ios, macos] Special-case $id, $type in predicates

Also support $id ≟ nil.
  • Loading branch information
jfirebaugh authored and 1ec5 committed Feb 9, 2017
1 parent 272dc3f commit a1a6391
Show file tree
Hide file tree
Showing 13 changed files with 781 additions and 65 deletions.
144 changes: 118 additions & 26 deletions include/mbgl/style/conversion/filter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ struct Converter<Filter> {
}

if (*op == "==") {
return convertBinaryFilter<EqualsFilter>(value);
return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value);
} else if (*op == "!=") {
return convertBinaryFilter<NotEqualsFilter>(value);
return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value);
} else if (*op == ">") {
return convertBinaryFilter<GreaterThanFilter>(value);
} else if (*op == ">=") {
Expand All @@ -39,42 +39,67 @@ struct Converter<Filter> {
} else if (*op == "<=") {
return convertBinaryFilter<LessThanEqualsFilter>(value);
} else if (*op == "in") {
return convertSetFilter<InFilter>(value);
return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value);
} else if (*op == "!in") {
return convertSetFilter<NotInFilter>(value);
return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value);
} else if (*op == "all") {
return convertCompoundFilter<AllFilter>(value);
} else if (*op == "any") {
return convertCompoundFilter<AnyFilter>(value);
} else if (*op == "none") {
return convertCompoundFilter<NoneFilter>(value);
} else if (*op == "has") {
return convertUnaryFilter<HasFilter>(value);
return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value);
} else if (*op == "!has") {
return convertUnaryFilter<NotHasFilter>(value);
return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value);
}

return Error { "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"" };
}

private:
Result<Value> normalizeValue(const std::string& key, const optional<Value>& value) const {
Result<Value> normalizeValue(const optional<Value>& value) const {
if (!value) {
return Error { "filter expression value must be a boolean, number, or string" };
} else if (key != "$type") {
} else {
return *value;
} else if (*value == std::string("Point")) {
return Value(uint64_t(FeatureType::Point));
} else if (*value == std::string("LineString")) {
return Value(uint64_t(FeatureType::LineString));
} else if (*value == std::string("Polygon")) {
return Value(uint64_t(FeatureType::Polygon));
}
}

template <class V>
Result<FeatureType> toFeatureType(const V& value) const {
optional<std::string> type = toString(value);
if (!type) {
return Error { "value for $type filter must be a string" };
} else if (*type == "Point") {
return FeatureType::Point;
} else if (*type == "LineString") {
return FeatureType::LineString;
} else if (*type == "Polygon") {
return FeatureType::Polygon;
} else {
return Error { "value for $type filter must be Point, LineString, or Polygon" };
}
}

template <class FilterType, class V>
template <class V>
Result<FeatureIdentifier> toFeatureIdentifier(const V& value) const {
optional<Value> identifier = toValue(value);
if (!identifier) {
return Error { "filter expression value must be a boolean, number, or string" };
} else {
return (*identifier).match(
[] (uint64_t t) -> Result<FeatureIdentifier> { return t; },
[] ( int64_t t) -> Result<FeatureIdentifier> { return t; },
[] ( double t) -> Result<FeatureIdentifier> { return t; },
[] (const std::string& t) -> Result<FeatureIdentifier> { return t; },
[] (const auto&) -> Result<FeatureIdentifier> {
return Error { "filter expression value must be a boolean, number, or string" };
});
}
}

template <class FilterType, class IdentifierFilterType, class V>
Result<Filter> convertUnaryFilter(const V& value) const {
if (arrayLength(value) < 2) {
return Error { "filter expression must have 2 elements" };
Expand All @@ -85,7 +110,48 @@ struct Converter<Filter> {
return Error { "filter expression key must be a string" };
}

return FilterType { *key };
if (*key == "$id") {
return IdentifierFilterType {};
} else {
return FilterType { *key };
}
}

template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
Result<Filter> convertEqualityFilter(const V& value) const {
if (arrayLength(value) < 3) {
return Error { "filter expression must have 3 elements" };
}

optional<std::string> key = toString(arrayMember(value, 1));
if (!key) {
return Error { "filter expression key must be a string" };
}

if (*key == "$type") {
Result<FeatureType> filterValue = toFeatureType(arrayMember(value, 2));
if (!filterValue) {
return filterValue.error();
}

return TypeFilterType { *filterValue };

} else if (*key == "$id") {
Result<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2));
if (!filterValue) {
return filterValue.error();
}

return IdentifierFilterType { *filterValue };

} else {
Result<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)));
if (!filterValue) {
return filterValue.error();
}

return FilterType { *key, *filterValue };
}
}

template <class FilterType, class V>
Expand All @@ -99,15 +165,15 @@ struct Converter<Filter> {
return Error { "filter expression key must be a string" };
}

Result<Value> filterValue = normalizeValue(*key, toValue(arrayMember(value, 2)));
Result<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)));
if (!filterValue) {
return filterValue.error();
}

return FilterType { *key, *filterValue };
}

template <class FilterType, class V>
template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
Result<Filter> convertSetFilter(const V& value) const {
if (arrayLength(value) < 2) {
return Error { "filter expression must at least 2 elements" };
Expand All @@ -118,16 +184,42 @@ struct Converter<Filter> {
return Error { "filter expression key must be a string" };
}

std::vector<Value> values;
for (std::size_t i = 2; i < arrayLength(value); ++i) {
Result<Value> filterValue = normalizeValue(*key, toValue(arrayMember(value, i)));
if (!filterValue) {
return filterValue.error();
if (*key == "$type") {
std::vector<FeatureType> values;
for (std::size_t i = 2; i < arrayLength(value); ++i) {
Result<FeatureType> filterValue = toFeatureType(arrayMember(value, i));
if (!filterValue) {
return filterValue.error();
}
values.push_back(*filterValue);
}
values.push_back(*filterValue);
}

return FilterType { *key, std::move(values) };
return TypeFilterType { std::move(values) };

} else if (*key == "$id") {
std::vector<FeatureIdentifier> values;
for (std::size_t i = 2; i < arrayLength(value); ++i) {
Result<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i));
if (!filterValue) {
return filterValue.error();
}
values.push_back(*filterValue);
}

return IdentifierFilterType { std::move(values) };

} else {
std::vector<Value> values;
for (std::size_t i = 2; i < arrayLength(value); ++i) {
Result<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)));
if (!filterValue) {
return filterValue.error();
}
values.push_back(*filterValue);
}

return FilterType { *key, std::move(values) };
}
}

template <class FilterType, class V>
Expand Down
101 changes: 100 additions & 1 deletion include/mbgl/style/filter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,95 @@ class NotHasFilter {
}
};


class TypeEqualsFilter {
public:
FeatureType value;

friend bool operator==(const TypeEqualsFilter& lhs, const TypeEqualsFilter& rhs) {
return lhs.value == rhs.value;
}
};

class TypeNotEqualsFilter {
public:
FeatureType value;

friend bool operator==(const TypeNotEqualsFilter& lhs, const TypeNotEqualsFilter& rhs) {
return lhs.value == rhs.value;
}
};

class TypeInFilter {
public:
std::vector<FeatureType> values;

friend bool operator==(const TypeInFilter& lhs, const TypeInFilter& rhs) {
return lhs.values == rhs.values;
}
};

class TypeNotInFilter {
public:
std::vector<FeatureType> values;

friend bool operator==(const TypeNotInFilter& lhs, const TypeNotInFilter& rhs) {
return lhs.values == rhs.values;
}
};


class IdentifierEqualsFilter {
public:
FeatureIdentifier value;

friend bool operator==(const IdentifierEqualsFilter& lhs, const IdentifierEqualsFilter& rhs) {
return lhs.value == rhs.value;
}
};

class IdentifierNotEqualsFilter {
public:
FeatureIdentifier value;

friend bool operator==(const IdentifierNotEqualsFilter& lhs, const IdentifierNotEqualsFilter& rhs) {
return lhs.value == rhs.value;
}
};

class IdentifierInFilter {
public:
std::vector<FeatureIdentifier> values;

friend bool operator==(const IdentifierInFilter& lhs, const IdentifierInFilter& rhs) {
return lhs.values == rhs.values;
}
};

class IdentifierNotInFilter {
public:
std::vector<FeatureIdentifier> values;

friend bool operator==(const IdentifierNotInFilter& lhs, const IdentifierNotInFilter& rhs) {
return lhs.values == rhs.values;
}
};

class HasIdentifierFilter {
public:
friend bool operator==(const HasIdentifierFilter&, const HasIdentifierFilter&) {
return true;
}
};

class NotHasIdentifierFilter {
public:
friend bool operator==(const NotHasIdentifierFilter&, const NotHasIdentifierFilter&) {
return true;
}
};


using FilterBase = variant<
class NullFilter,
class EqualsFilter,
Expand All @@ -159,7 +248,17 @@ using FilterBase = variant<
class AllFilter,
class NoneFilter,
class HasFilter,
class NotHasFilter>;
class NotHasFilter,
class TypeEqualsFilter,
class TypeNotEqualsFilter,
class TypeInFilter,
class TypeNotInFilter,
class IdentifierEqualsFilter,
class IdentifierNotEqualsFilter,
class IdentifierInFilter,
class IdentifierNotInFilter,
class HasIdentifierFilter,
class NotHasIdentifierFilter>;

class Filter : public FilterBase {
public:
Expand Down
Loading

0 comments on commit a1a6391

Please sign in to comment.