Sometimes, specific terms can act as cues that you can use to filter down the hits that your users get.
Consider a restaurant’s website where users can search for meals and get them delivered to their home.
If a user types “gluten-free”, you could use this term to filter out any meal that has “gluten” in its list of allergens.
To do this, you can use Algolia’s Rules and trigger custom filters based on what users search for.
Positive filters
If you want to filter out every non-diet-friendly meal whenever user’s search queries contain the term “diet”,
you could use the _tags
attribute to categorize meals depending on their individual qualities:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| [
{
"name": "Chicken Stuffed Baked Avocados",
"restaurant": "The Hive",
"_tags": ["low-carb"]
},
{
"name": "Spinach Quiche",
"restaurant": "Bert's Inn",
"_tags": ["low-carb", "vegetarian"]
},
{
"name": "Pizza Chicken Bake",
"restaurant": "Millbrook Deli",
"_tags": ["cheese"]
},
{
"name": "Strawberry Sorbet",
"restaurant": "The Hive",
"_tags": ["low-fat", "vegetarian", "vegan"]
}
]
|
When users include the term “diet” in their search, you want to automatically return every record that has “low-carb” or “low-fat” in their _tags
attribute. Because _tags
is already optimized for filtering, you don’t have to set it as an attribute for faceting. You can directly create a new Rule that detects the term “diet” in a query and applies a positive filter on tags “low-carb” and “low-fat”.
You also need to add a consequence in your rule to remove the word “diet” from your query. This way, it won’t be used as a search term, only for filtering.
Using the API
To add a Rule, you need to use the saveRule
method. When setting a Rule, you need to define a condition and a consequence.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| $rule = [
'objectID' => 'diet-rule',
'conditions' => array(array(
'pattern' => 'diet',
'anchoring' => 'contains',
)),
'consequence' => [
'params' => [
'filters' => '"low-carb" OR "low-fat"',
'query' => [
'edits' => [
[
'type' => 'remove',
'delete' => 'diet'
]
]
]
]
]
];
$response = $index->saveRule($rule);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| rule = {
objectID: 'diet-rule',
conditions: [{
pattern: 'diet',
anchoring: 'contains'
}],
consequence: {
params: {
filters: '"low-carb" OR "low-fat"',
query: {
edits: [
{
type: 'remove',
delete: 'diet'
}
]
}
}
}
}
index.save_rule(rule)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| const rule = {
objectID: 'diet-rule',
conditions: [{
pattern: 'diet',
anchoring: 'contains'
}],
consequence: {
params: {
filters: '"low-carb" OR "low-fat"',
query: {
edits: [
{
type: 'remove',
delete: 'diet'
}
]
}
}
}
};
index.saveRule(rule).then(() => {
// done
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| rule = {
'objectID': 'diet-rule',
'conditions': [{
'pattern': 'diet',
'anchoring': 'contains'
}],
'consequence': {
'params': {
'filters': '"low-carb" OR "low-fat"',
'query': {
'edits': [{
'type': 'remove',
'delete': 'diet'
}]
}
}
}
}
response = index.save_rule(rule)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| let rule = Rule(objectID: "diet-rule")
.set(\.conditions, to: [
Rule.Condition()
.set(\.anchoring, to: .contains)
.set(\.pattern, to: .literal("diet"))
])
.set(\.consequence, to: Rule.Consequence()
.set(\.queryTextAlteration, to: .edits([.remove("diet")]))
.set(\.query, to: Query().set(\.filters, to: "\"low-carb\" OR \"low-fat\""))
)
index.saveRule(rule) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| val rules = rules {
rule(
"diet-rule",
listOf(Condition(Contains, Literal("diet"))),
Consequence(
edits = edits { +"diet" },
query = query {
filters {
orTag {
tag("low-carb")
tag("low-fat")
}
}
}
)
)
}
index.saveRules(rules)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| var rule = new Rule
{
ObjectID = "diet-rule",
Conditions = new List<Condition>
{
new Condition { Anchoring = "contains", Pattern = "diet" }
},
Consequence = new Consequence
{
Params = new ConsequenceParams
{
Filters = "'low-carb' OR 'low-fat'"
Edits = new List<Edit>
{
new Edit {Type = EditType.Remove, Delete = "diet"}
}
}
}
};
index.SaveRule(rule);
// Asynchronous
await index.SaveRuleAsync(rule);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| Condition condition = new Condition()
.setPattern("diet")
.setAnchoring("contains");
ConsequenceQuery consequenceQuery =
new ConsequenceQuery().setEdits(Collections.singletonList(new Edit().setType("remove").setDelete("diet")));
ConsequenceParams params = new ConsequenceParams();
params.setConsequenceQuery(consequenceQuery);
params.setFilters("'low-carb' OR 'low-fat'");
Consequence consequence = new Consequence().setParams(params);
Rule rule = new Rule()
.setObjectID("diet-rule")
.setCondition(Collections.singletonList(condition))
.setConsequence(consequence);
index.saveRule(rule);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| rule := search.Rule{
ObjectID: "diet-rule",
Condition: []search.RuleCondition{{Anchoring: search.Contains, Pattern: "diet"}},
Consequence: search.RuleConsequence{
Params: &search.RuleParams{
Query: search.NewRuleQueryObject(
search.RuleQueryObjectQuery{
Edits: []search.QueryEdit{
search.RemoveEdit("diet"),
},
},
),
QueryParams: search.QueryParams{
Filters: opt.Filters("'low-carb' OR 'low-fat'"),
},
},
},
}
res, err := index.SaveRule(rule)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| val ruleToSave = Rule(
objectID = "diet-rule",
conditions = Some(Seq(Condition(
pattern = "diet",
anchoring = "contains"
))),
consequence = Consequence(
params = Seq(
Map("filters" -> "'low-carb' OR 'low-fat'"),
Map(
"query" -> Map(
"edits" -> Seq(
Edit("remove", "diet")
)
)
)
)
),
)
client.execute {
save rule ruleToSave inIndex "index_name"
}
|
Using the dashboard
You can also add your Rules in your Algolia dashboard.
- Select the Search product icon on your dashboard.
- Select the Rules section from the left sidebar menu in the Algolia dashboard.
- Under the heading Rules, select the index to which you’re adding a Rule.
- Select Create your first rule or New rule. In the dropdown, click the Manual Editor option.
- In the Condition(s) sections, keep Query contains and enter “diet” in the input field.
- In the Consequence(s) section:
- Click the Add consequence button and select Add Query Parameter.
- In the input field that appears, enter the JSON search parameter you want to add. For example:
{ "filters": "'low-carb' OR 'low-fat'" }
.
- Click the Add consequence button again and select Remove Word.
- Type or select “diet” in the input field.
- Don’t forget to save your changes.
Negative filters
Consider filtering out every meal that’s not gluten-free when the users’ search queries include the term “gluten-free”.
You could add “Gluten-free” in the title (for example, “Gluten-free Pasta Dough”), but this would force you to do it for every meal that doesn’t contain gluten, even those that don’t typically do (for example, “Leek Soup” or “Fruit Salad”). When a user is looking for “gluten-free” meals, it’s safer to assume they want everything that matches their search and doesn’t have gluten, not that they’re looking for meals with the words “Gluten-free” in the title.
You could maintain a list of tags (with the special _tags
attribute), but you may want to separate allergens, especially if you don’t want to make them searchable.
Instead, a better approach is to create a more definitive list of all allergens:
1
2
3
4
5
6
7
8
9
10
11
12
| [
{
"name": "Pasta Bolognese",
"restaurant": "Millbrook Deli",
"allergens": ["eggs", "lactose"]
},
{
"name": "Breakfast Waffles",
"restaurant": "The Hive",
"allergens": ["gluten", "lactose"]
}
]
|
When users add “gluten-free” in their search, you want to automatically filter out every record with “gluten” in its allergens
attribute. To do so, you first need to set allergens
in your list of attributes for faceting. Then, you can create a new Rule that filters out unwanted records based on the allergens
facet values.
Using the API
First, you must set allergens
as an attributesForFaceting
in your index:.
1
2
3
4
5
| $index->setSettings([
'attributesForFaceting' => [
"allergens"
]
]);
|
1
2
3
4
5
| index.set_settings({
attributesForFaceting: [
'allergens'
]
})
|
1
2
3
4
5
6
7
| index.setSettings({
attributesForFaceting: [
'allergens'
]
}).then(() => {
// done
});
|
1
2
3
4
5
| index.set_settings({
'attributesForFaceting': [
'allergens'
]
})
|
1
2
3
4
5
6
7
8
| let settings = Settings()
.set(\.attributesForFaceting, to: ["allergens"])
index.setSettings(settings) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
|
1
2
3
4
5
6
7
| val settings = settings {
attributesForFaceting {
+"allergens"
}
}
index.setSettings(settings)
|
1
2
3
4
5
6
7
8
9
10
11
12
| IndexSettings settings = new IndexSettings
{
AttributesForFaceting = new List<string>
{
"allergens"
}
};
index.SetSettings(settings);
// Asynchronous
await index.SetSettingsAsync(settings);
|
1
| index.setSettings(new IndexSettings().setAttributesForFaceting(Collections.singletonList("allergens")));
|
1
2
3
| res, err := index.SetSettings(search.Settings{
AttributesForFaceting: opt.AttributesForFaceting("allergens"),
})
|
1
2
3
4
5
6
7
| client.execute {
changeSettings of "myIndex" `with` IndexSettings(
attributesForFaceting = Some(Seq(
"allergens"
))
)
}
|
Then, you can set a Rule that detects the term “gluten-free” in a query and applies a negative filter on facet value allergens:gluten
. For this, you need to use the saveRule
method.
You must also add a consequence in your Rule to remove the word “gluten-free” from your query. This way, it won’t be used as a search term, only for filtering purposes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| $rule = [
'objectID' => 'gluten-free-rule',
'conditions' => array(array(
'pattern' => 'gluten-free',
'anchoring' => 'contains',
))
'consequence' => [
'params' => [
'filters' => 'NOT allergens:gluten',
'query' => [
'edits' => [
'type' => 'remove',
'delete' => 'gluten-free'
]
]
]
]
];
$response = $index->saveRule($rule['objectID'], $rule);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| rule = {
objectID: 'gluten-free-rule',
conditions: [{
pattern: 'gluten-free',
anchoring: 'contains'
}],
consequence: {
params: {
filters: 'NOT allergens:gluten',
query: {
edits: [
type: 'remove',
delete: 'gluten-free'
]
}
}
}
}
index.save_rule(rule)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| const rule = {
objectID: 'gluten-free-rule',
conditions: [{
pattern: 'gluten-free',
anchoring: 'contains'
}],
consequence: {
params: {
filters: 'NOT allergens:gluten',
query: {
edits: [
type: 'remove',
delete: 'gluten-free'
]
}
}
}
};
index.saveRule(rule).then(() => {
// done
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| rule = {
'objectID': 'gluten-free-rule',
'conditions': [{
'pattern': 'gluten-free',
'anchoring': 'contains'
}],
'consequence': {
'params': {
'filters': 'NOT allergens:gluten',
'query': {
'edits': [{
'type': 'remove',
'delete': 'gluten-free'
}]
}
}
}
}
response = index.save_rule(rule)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| let rule = Rule(objectID: "gluten-free-rule")
.set(\.conditions, to: [
Rule.Condition()
.set(\.anchoring, to: .contains)
.set(\.pattern, to: .literal("gluten-free"))
])
.set(\.consequence, to: Rule.Consequence()
.set(\.queryTextAlteration, to: .edits([.remove("gluten-free")]))
.set(\.query, to: Query().set(\.filters, to: "NOT allergens:gluten"))
)
index.saveRule(rule) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| val rules = rules {
rule(
"gluten-free-rule",
listOf(Condition(Contains, Literal("gluten-free"))),
Consequence(
edits = edits { +"gluten-free" },
query = query {
filters { and { facet("allergens", "gluten", isNegated = true) } }
}
)
)
}
index.saveRules(rules)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| var rule = new Rule
{
ObjectID = "gluten-free-rule",
Conditions = new List<Condition>
{
new Condition { Anchoring = "contains", Pattern = "gluten-free" }
},
Consequence = new Consequence
{
Params = new ConsequenceParams
{
Filters = "NOT allergens:gluten"
Edits = new List<Edit>
{
new Edit {Type = EditType.Remove, Delete = "gluten-free"}
}
}
}
};
index.SaveRule(rule);
// Asynchronous
await index.SaveRuleAsync(rule);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| Condition condition = new Condition().setPattern("gluten-free").setAnchoring("contains");
ConsequenceQuery consequenceQuery =
new ConsequenceQuery().setEdits(Collections.singletonList(new Edit().setType("remove").setDelete("gluten-free")));
ConsequenceParams params = new ConsequenceParams();
params.setConsequenceQuery(consequenceQuery);
params.setFilters("NOT allergens:gluten");
Consequence consequence = new Consequence().setParams(params);
Rule rule =
new Rule()
.setObjectID("gluten-free-rule")
.setCondition(Collections.singletonList(condition))
.setConsequence(consequence);
index.saveRule(rule);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| rule := search.Rule{
ObjectID: "gluten-free-rule",
Conditions: []search.RuleCondition{{Anchoring: search.Contains, Pattern: "gluten-free"}},
Consequence: search.RuleConsequence{
Params: &search.RuleParams{
Query: search.NewRuleQueryObject(
search.RuleQueryObjectQuery{
Edits: []search.QueryEdit{
search.RemoveEdit("gluten-free"),
},
},
),
QueryParams: search.QueryParams{
Filters: opt.Filters("NOT allergens:gluten"),
},
},
},
}
res, err := index.SaveRule(rule)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| val ruleToSave = Rule(
objectID = "gluten-free-rule",
conditions = Some(Seq(Condition(
pattern = "gluten-free",
anchoring = "contains"
))),
consequence = Consequence(
params = Seq(
Map("filters" -> "NOT allergens:gluten"),
Map(
"query" -> Map(
"edits" -> Seq(
Edit("remove", "gluten-free")
)
)
)
)
),
)
client.execute {
save rule ruleToSave inIndex "index_name"
}
|
Using the dashboard
You can also add your Rules in your Algolia dashboard.
- Select the Search product icon on your dashboard and then select your index.
- Click the Configuration tab.
- In the Facets subsection of Filtering and Faceting, click the “Add an attribute” button and select the
allergens
attribute from the dropdown.
- Click the Rules tab.
- Select Create your first rule or New rule. In the dropdown, click the Manual Editor option.
- In the Condition(s) section, keep Query toggled on, select Contains in the dropdown, and enter “gluten-free” in the input field.
- In the Consequence(s) section:
- Click the Add consequence button and select Add Query Parameter.
- In the input field that appears, enter the JSON search parameter you want to add. For example:
{ "filters": "NOT allergens:gluten" }
- Click the Add consequence button again and select Remove Word.
- Type or select “gluten-free” in the input field.
- Save your changes.
Numerical filtering
Consider the query “cheap toaster 800w”. You can use Rules to filter the results by “toaster” and “prices between 0 and 25” so that the only textual search is the remaining term, “800w”, which could further be used to limit the results with that wattage.
Rule
If query = “cheap toaster” then price < 10 and type=toaster
This requires two rules.
Using the API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // Turn JSON into an array
$rules = array(
array(
'objectID' => 'cheap',
'condition' => array(
'pattern' => 'cheap',
'anchoring' => 'contains'
),
'consequence' => array(
'params' => array(
'query' => array(
'remove' => 'cheap'
),
'filters' => 'price < 10'
)
)
)
);
// Push Rule to index
$index->batchRules($rules);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| rule = {
objectID: 'cheap',
condition: {
pattern: 'cheap',
anchoring: 'contains'
},
consequence: {
params: {
query: {
remove: 'cheap'
}
},
filters: 'price < 10'
}
}
index.save_rule(rule['objectID'], rule)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| index.saveRule({
objectID: 'cheap',
condition: {
pattern: 'cheap',
anchoring: 'contains',
},
consequence: {
params: {
query: {
remove: 'cheap',
},
},
filters: 'price < 10',
},
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| rule = {
'objectID': 'cheap',
'condition': {
'pattern': 'cheap',
'anchoring': 'contains'
},
'consequence': {
'params': {
'query': {
'remove': 'cheap'
}
}
},
'filters': 'price < 10'
}
response = index.save_rule(rule)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| let rule = Rule(objectID: "cheap")
.set(\.conditions, to: [
Rule.Condition()
.set(\.anchoring, to: .contains)
.set(\.pattern, to: .literal("cheap"))
])
.set(\.consequence, to: Rule.Consequence()
.set(\.queryTextAlteration, to: .edits([.remove("cheap")]))
.set(\.query, to: Query().set(\.filters, to: "price < 10"))
)
index.saveRule(rule) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| val rules = rules {
rule(
"cheap",
conditions {
+condition(Contains, Literal("cheap"))
},
consequence(
edits = edits { +"cheap" },
query = query {
filters {
and { comparison("price", Less, 10) }
}
}
)
)
}
index.saveRules(rules)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| var rule = new Rule
{
ObjectID = "cheap",
Conditions = new List<Condition> {
new Condition { Anchoring = "contains", Pattern = "cheap" }
},
Consequence = new Consequence
{
Params = new ConsequenceParams {
Filters = "price < 10",
Query = new ConsequenceQuery {
Edits = new List<Edit> {
new Edit {
Type = EditType.Remove,
Delete = "cheap"
}
}
}
}
}
};
index.SaveRule(rule);
// Asynchronous
await index.SaveRuleAsync(rule);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| Condition condition = new Condition()
.setPattern("cheap")
.setAnchoring("contains");
ConsequenceQuery consequenceQuery = new ConsequenceQuery()
.setEdits(Collections.singletonList(new Edit().setType("remove").setDelete("cheap")));
ConsequenceParams params = new ConsequenceParams()
.setConsequenceQuery(consequenceQuery).setFilters("price < 10");
Consequence consequence = new Consequence()
.setParams(params);
Rule rule = new Rule()
.setObjectID("cheap")
.setConditions(Collections.singletonList(condition))
.setConsequence(consequence);
// Synchronous
index.saveRule(rule);
// Asynchronous
index.saveRuleAsync(rule);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| rule := search.Rule{
ObjectID: "cheap",
Condition: search.RuleCondition{Anchoring: search.Contains, Pattern: "cheap"},
Consequence: search.RuleConsequence{
Params: &search.RuleParams{
Query: search.NewRuleQueryObject(
search.RuleQueryObjectQuery{
Edits: []search.QueryEdit{
search.RemoveEdit("cheap"),
},
},
),
QueryParams: search.QueryParams{
OptionalFilters: opt.Filter("price < 10"),
},
},
},
}
res, err := index.SaveRule(rule)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| val ruleToSave = Rule(
objectID = "cheap",
condition = Condition(
pattern = "cheap",
anchoring = "contains"
),
consequence = Consequence(
params = Seq(
Map("Filters" -> "price < 10"),
Map(
"query" -> Map(
"edits" -> Seq(
Edit("remove", "cheap")
)
)
)
)
),
)
client.execute {
save rule ruleToSave inIndex "index_name"
}
|
Using the dashboard
Since there are two Rules, you’ll need to set up both separately.
Preparation
- Select the Search product icon on your dashboard and then select your index.
- Click the Configuration tab.
- In the Facets subsection of Filtering and Faceting, click the “Add an attribute” button and select the
product_type
attribute from the dropdown.
For the first rule
- Select the Rules section from the left sidebar menu in the Algolia dashboard.
- Under the heading Rules, select the index to which you’re adding a rule.
- Select Create your first rule or New rule. In the dropdown, click the Manual Editor option.
- In the Condition(s) section, keep Query toggled on, select Contains in the dropdown, and enter “toaster” in the input field.
- In the Consequence(s) section:
- Click the Add consequence button and select Add Query Parameter.
- In the input field that appears, add the JSON parameters you want to apply when the user’s query matches the Rule:
{ "filters": "product_type:toaster" }
- Click the Add consequence button again and select Remove Word.
- Type or select “toaster” in the input field.
- Save your changes.
For the second rule
- Go back to the Rules section.
- Select New rule. In the dropdown, click the Manual Editor option.
- In the Condition(s) section, keep Query toggled on, select Contains in the dropdown, and enter “cheap” in the input field.
- In the Consequence(s) section:
- Click the Add consequence button and select Add Query Parameter.
- In the input field that appears, add the JSON parameters you want to apply when the user’s query matches the Rule:
{ "filters": "price<10" }
- Click the Add consequence button again and select Remove Word.
- Type or select “cheap” in the input field.
- Save your changes.