Smart Segments

What is a Smart Segment?

A Segment is a subset of a collection: it's basically a saved filter of your collection.
Segments are designed for those who want to systematically visualize data according to specific sets of filters. It allows you to save your filters configuration so you don’t have to compute the same actions every day.
A Smart Segments is useful when you want to use a complex filter, which you'll add as code in your backend.

Creating a Smart Segment

Sometimes, segment filters are complicated and closely tied to your business. Forest Admin allows you to code how the segment is computed.
On our Live Demo example, we’ve implemented a Smart Segment on the collection products to allow admin users to see the bestsellers at a glance.
SQL
Mongodb
Rails
Django
Laravel
You’re free to implement the business logic you need. The only requirement is to return a valid Sequelize condition. Most of the time, your Smart Segment should return something like { id: { in: [ 1,2,3,4,5 ] } }.
On our implementation, we use a raw SQL query to filter and sort the product that was sold the most.
/forest/products.js
1
const { collection } = require('forest-express-sequelize');
2
const models = require('../models');
3
4
const { Op, QueryTypes } = models.objectMapping;
5
6
collection('products', {
7
segments: [{
8
name: 'Bestsellers',
9
where: (product) => {
10
return models.connections.default.query(`
11
SELECT products.id, COUNT(orders.*)
12
FROM products
13
JOIN orders ON orders.product_id = products.id
14
GROUP BY products.id
15
ORDER BY count DESC
16
LIMIT 5;
17
`, { type: QueryTypes.SELECT })
18
.then((products) => {
19
let productIds = products.map((product) => product.id);
20
return { id: { [Op.in]: productIds }};
21
});
22
}
23
}]
24
});
Copied!
You’re free to implement the business logic you need. Your Smart Segment should return something like { _id: { $in: [ 1,2,3,4,5 ] } }.
/forest/products.js
1
const { collection } = require('forest-express-mongoose');
2
const { Product } = require('../models');
3
4
collection('Product', {
5
fields: [{
6
field: 'buyers',
7
type: ['String'],
8
reference: 'Customer'
9
}],
10
segments: [{
11
name: 'Bestsellers',
12
where: (product) => {
13
return Product
14
.aggregate([
15
{
16
$project: { orders_count: {$size: { "$ifNull": [ "$orders", [] ] } } }
17
},
18
{
19
$sort: {"orders_count":-1}
20
},
21
{
22
$limit: 5
23
}
24
])
25
.then((products) => {
26
let productIds = [];
27
products.filter((product) => {
28
if (product._id.length === 0) { return false; }
29
return true;
30
})
31
.forEach((product) => {
32
productIds.push(product._id);
33
});
34
return {"_id": { $in: productIds} };
35
});
36
}
37
}]
38
});
Copied!
/lib/forest_liana/collections/product.rb
1
class Forest::Product
2
include ForestLiana::Collection
3
4
collection :Product
5
6
segment 'Bestsellers' do
7
productIds = Product.joins(:orders).group('products.id').order('count(orders.id)').limit(10).pluck('products.id')
8
9
{ id: productIds }
10
end
11
end
Copied!
app/forest/product.py
1
from django.db.models import Q
2
from django_forest.utils.collection import Collection
3
4
from app.models import Product
5
6
7
class ProductForest(Collection):
8
def load(self):
9
10
self.segments = [
11
{
12
'name': 'Best sellers',
13
'where': self.best_sellers
14
}
15
]
16
17
18
def best_sellers(self):
19
products = Question.objects.raw('''SELECT app_product.id, COUNT(app_order.*)
20
FROM app_product
21
JOIN app_order ON app_order.product_id = app_product.id
22
GROUP BY app_product.id
23
ORDER BY count DESC
24
LIMIT 5;''')
25
return Q(**{'id__in': [product.id for product in products]})
26
27
Collection.register(ProductForest, Product)
Copied!
The 2nd parameter of the SmartSegment method is not required. If you don't fill it, the name of your SmartSegment will be the name of your method that wrap it.
app/Models/Product.php
1
<?php
2
3
namespace App\Models;
4
5
use ForestAdmin\LaravelForestAdmin\Services\Concerns\ForestCollection;
6
use ForestAdmin\LaravelForestAdmin\Services\SmartFeatures\SmartSegment;
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Database\Eloquent\Factories\HasFactory;
9
use Illuminate\Database\Eloquent\Model;
10
11
class Product extends Model
12
{
13
use HasFactory
14
use ForestCollection;
15
16
/**
17
* @return SmartSegment
18
*/
19
public function bestSellers(): SmartSegment
20
{
21
return $this->smartSegment(
22
fn(Builder $query) => $query->whereIn('products.id', function($q) {
23
$q->select('products.id')
24
->from('products')
25
->join('order_product', 'order_product.product_id', '=', 'products.id')
26
->groupBy('products.id')
27
->orderByRaw('COUNT(order_product.order_id) DESC')
28
->limit(10);
29
}),
30
'Best sellers'
31
);
32
}
Copied!

Setting up independent columns visibility

By default, Forest Admin applies the same configuration to all segments of the same collection.
However, the Independent columns configuration option allows you to display different columns on your different segments.