Skip to content

Latest commit

 

History

History
281 lines (216 loc) · 8.32 KB

smart-segments.md

File metadata and controls

281 lines (216 loc) · 8.32 KB

{% hint style="warning" %} Please be sure of your agent type and version and pick the right documentation accordingly. {% endhint %}

{% tabs %} {% tab title="Node.js" %} {% hint style="danger" %} This is the documentation of the forest-express-sequelize and forest-express-mongoose Node.js agents that will soon reach end-of-support.

forest-express-sequelize v9 and forest-express-mongoose v9 are replaced by @forestadmin/agent v1.

Please check your agent type and version and read on or switch to the right documentation. {% endhint %} {% endtab %}

{% tab title="Ruby on Rails" %} {% hint style="success" %} This is still the latest Ruby on Rails documentation of the forest_liana agent, you’re at the right place, please read on. {% endhint %} {% endtab %}

{% tab title="Python" %} {% hint style="danger" %} This is the documentation of the django-forestadmin Django agent that will soon reach end-of-support.

If you’re using a Django agent, notice that django-forestadmin v1 is replaced by forestadmin-agent-django v1.

If you’re using a Flask agent, go to the forestadmin-agent-flask v1 documentation.

Please check your agent type and version and read on or switch to the right documentation. {% endhint %} {% endtab %}

{% tab title="PHP" %} {% hint style="danger" %} This is the documentation of the forestadmin/laravel-forestadmin Laravel agent that will soon reach end-of-support.

If you’re using a Laravel agent, notice that forestadmin/laravel-forestadmin v1 is replaced by forestadmin/laravel-forestadmin v3.

If you’re using a Symfony agent, go to the forestadmin/symfony-forestadmin v1 documentation.

Please check your agent type and version and read on or switch to the right documentation. {% endhint %} {% endtab %} {% endtabs %}

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.

{% tabs %} {% tab title="SQL" %} 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.

{% code title="/forest/products.js" %}

const { collection } = require('forest-express-sequelize');
const models = require('../models');

const { Op, QueryTypes } = models.objectMapping;

collection('products', {
  segments: [
    {
      name: 'Bestsellers',
      where: (product) => {
        return models.connections.default
          .query(
            `
        SELECT products.id, COUNT(orders.*)
        FROM products
        JOIN orders ON orders.product_id = products.id
        GROUP BY products.id
        ORDER BY count DESC
        LIMIT 5;
      `,
            { type: QueryTypes.SELECT }
          )
          .then((products) => {
            let productIds = products.map((product) => product.id);
            return { id: { [Op.in]: productIds } };
          });
      },
    },
  ],
});

{% endcode %} {% endtab %}

{% tab title="Mongodb" %} You’re free to implement the business logic you need. Your Smart Segment should return something like { _id: { $in: [ 1,2,3,4,5 ] } }.

{% code title="/forest/products.js" %}

const { collection } = require('forest-express-mongoose');
const { Product } = require('../models');

collection('Product', {
  fields: [
    {
      field: 'buyers',
      type: ['String'],
      reference: 'Customer',
    },
  ],
  segments: [
    {
      name: 'Bestsellers',
      where: (product) => {
        return Product.aggregate([
          {
            $project: { orders_count: { $size: { $ifNull: ['$orders', []] } } },
          },
          {
            $sort: { orders_count: -1 },
          },
          {
            $limit: 5,
          },
        ]).then((products) => {
          let productIds = [];
          products
            .filter((product) => {
              if (product._id.length === 0) {
                return false;
              }
              return true;
            })
            .forEach((product) => {
              productIds.push(product._id);
            });
          return { _id: { $in: productIds } };
        });
      },
    },
  ],
});

{% endcode %} {% endtab %}

{% tab title="Rails" %} {% code title="/lib/forest_liana/collections/product.rb" %}

class Forest::Product
  include ForestLiana::Collection

  collection :Product

  segment 'Bestsellers' do
    productIds = Product.joins(:orders).group('products.id').order('count(orders.id)').limit(10).pluck('products.id')

    { id: productIds }
  end
end

{% endcode %} {% endtab %}

{% tab title="Django" %} {% code title="app/forest/product.py" %}

from django.db.models import Q
from django_forest.utils.collection import Collection

from app.models import Product


class ProductForest(Collection):
    def load(self):

        self.segments = [
            {
                'name': 'Best sellers',
                'where': self.best_sellers
            }
        ]


    def best_sellers(self):
        products = Question.objects.raw('''SELECT app_product.id, COUNT(app_order.*)
            FROM app_product
            JOIN app_order ON app_order.product_id = app_product.id
            GROUP BY app_product.id
            ORDER BY count DESC
            LIMIT 5;''')
        return Q(**{'id__in': [product.id for product in products]})

Collection.register(ProductForest, Product)

{% endcode %}

Ensure the file app/forest/__init__.py exists and contains the import of the previous defined class :

{% code title="app/forest/init.py" %}

from app.forest.product import ProductForest

{% endcode %} {% endtab %}

{% tab title="Laravel" %} {% hint style="info" %} 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. {% endhint %}

{% code title="app/Models/Product.php" %}

<?php

namespace App\Models;

use ForestAdmin\LaravelForestAdmin\Services\Concerns\ForestCollection;
use ForestAdmin\LaravelForestAdmin\Services\SmartFeatures\SmartSegment;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory
    use ForestCollection;

    /**
     * @return SmartSegment
     */
    public function bestSellers(): SmartSegment
    {
        return $this->smartSegment(
            fn(Builder $query) => $query->whereIn('products.id', function($q) {
                $q->select('products.id')
                    ->from('products')
                    ->join('order_product', 'order_product.product_id', '=', 'products.id')
                    ->groupBy('products.id')
                    ->orderByRaw('COUNT(order_product.order_id) DESC')
                    ->limit(10);
            }),
            'Best sellers'
        );
    }

{% endcode %} {% endtab %} {% endtabs %}

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.