Wstęp
Jeśli przeglądałeś już definicje pól w Odoo, na pewno natknąłeś się kiedyś na parametr index=True. Wygląda jak drobny dodatek, ale potrafi znacząco przyspieszyć wyszukiwanie i filtrowanie, gdy baza danych rośnie do dziesiątek czy setek tysięcy rekordów.
Ten tekst wyjaśnia, czym w praktyce jest pole indeksowane w modelu danych Odoo, jaki ma wpływ na strukturę bazy i kiedy warto z niego korzystać podczas tworzenia modułów czy przeglądu istniejącego systemu. Zrozumienie tej koncepcji ułatwi podejmowanie świadomych decyzji projektowych i optymalizacyjnych.
Czym jest pole z indeksem w Odoo
W ORM Odoo pole uznaje się za indeksowane, gdy w jego definicji w Pythonie podano index=True. Odoo wtedy instruuje PostgreSQL, by przy instalacji lub aktualizacji modułu utworzył indeks B-tree na odpowiedniej kolumnie w bazie danych.
Poniżej pokazano skrócony przykład takiej definicji w kodzie modułu Odoo:
class SaleOrder(models.Model):
_name = 'sale.order'
reference = fields.Char(string='Reference', index=True)
state = fields.Selection([...], index=True)
partner_id = fields.Many2one('res.partner', index=True)
Dla użytkownika interfejsu nic się nie zmienia wizualnie — indeksowanie to mechanizm działający po stronie bazy danych. Formularze i listy wyglądają tak samo, niezależnie od tego, czy kolumna ma indeks, czy nie.
Różnica pojawia się przy wydajności: indeks pozwala PostgreSQL odnaleźć dopasowania znacznie szybciej, zwłaszcza na dużych tabelach. Bez indeksu silnik bazy musi przeszukać każdy wiersz; z indeksem porusza się po uporządkowanej strukturze i trafia bezpośrednio tam, gdzie są pasujące rekordy.
Które typy pól w Odoo obsługują index=True
Większość prostych (skalarowych) pól w ORM Odoo przyjmuje atrybut index:
- Pola Char i Text
- Pola Integer i Float
- Pola Date i Datetime
- Pola Selection
- Pola Many2one (bardzo często indeksowane)
- Pola Boolean
Pola relacyjne typu One2many i Many2many nie mają bezpośredniej kolumny, którą można zwyczajnie indeksować, więc atrybut index dla nich nie ma zastosowania. Podobnie pola obliczane, które nie są przechowywane (store=False), nie mają fizycznej kolumny, więc nie można ich indeksować.
Jak działa takie pole
Podczas inicjalizacji lub aktualizacji modułu Odoo czyta definicje pól i synchronizuje schemat bazy. Dla każdego pola z index=True Odoo wykonuje odpowiednie polecenie SQL, tworząc indeks na kolumnie w PostgreSQL.
Domyślnie PostgreSQL tworzy indeks typu B-tree — to najbardziej uniwersalny rodzaj indeksu, dobrze sprawdzający się przy porównaniach równych (=), zapytaniach zakresowych (>, <, BETWEEN) oraz sortowaniu. Takie operacje to większość tego, co Odoo robi przy filtrowaniu w widokach listy i wyszukiwaniu po domenach.
Jak to współgra z ORM Odoo
ORM Odoo tłumaczy domeny napisane w Pythonie na zapytania SQL. Domena typu [('state', '=', 'sale')] przekłada się na WHERE state = 'sale'. Jeśli pole state ma indeks, PostgreSQL użyje go, by rozwiązać warunek szybko, bez pełnego skanowania tabeli.
Pola Many2one to typowy scenariusz: w tabeli zamówień partner_id przechowuje identyfikator klienta. Gdy lista jest filtrowana po kliencie, zapytanie zawiera WHERE partner_id = X. Indeks na tej kolumnie pozwala błyskawicznie znaleźć pasujące zamówienia nawet przy setkach tysięcy rekordów.
Indeksy a operacje zapisu
Indeksy mają cenę: przy każdym INSERT, UPDATE lub DELETE PostgreSQL musi zaktualizować wszystkie indeksy danej tabeli. Im więcej indeksów, tym wolniejsze zapisy. Dla większości zastosowań Odoo kompromis jest opłacalny, ale indeksowanie absolutnie wszystkiego to zły pomysł — warto ważyć korzyści z odczytu przeciw kosztom przy zapisie.
Opcja index='trigram'
W Odoo 16+ atrybut index może przyjmować też wartość 'trigram'. Tworzy to indeks GIN oparty na rozszerzeniu pg_trgm, który znacznie przyspiesza wyszukiwanie wzorców przy zapytaniach ILIKE. To przydatne przy polach tekstowych wyszukiwanych częściowo, np. nazwy produktów czy kontrahentów.
name = fields.Char(string='Product Name', index='trigram')
To bardziej zaawansowane rozwiązanie stosowane w standardowych modułach Odoo tam, gdzie pola są często wyszukiwane po fragmencie tekstu.
Przykłady zastosowań w firmie
Pola indeksowane mają praktyczne znaczenie w wielu scenariuszach biznesowych. Poniżej pięć sytuacji, w których indeks naprawdę poprawia komfort pracy.
1. CRM: filtrowanie leadów po odpowiedzialnym handlowcu
W CRM kierownicy regularnie filtrują lejki po handlowcu. Pole user_id na crm.lead jest domyślnie indeksowane, więc filtrowanie działa szybko nawet przy dużej liczbie leadów. To samo dotyczy niestandardowych Many2one wskazujących na użytkownika czy zespół — warto je indeksować, jeśli będą używane do filtrowania.
2. Sprzedaż: wyszukiwanie zamówień po statusie
Pole state na sale.order jest indeksowane, co pozwala sprawnie uzyskać listę zamówień w konkretnym stanie (np. potwierdzone, oczekujące na wysyłkę). Pola typu selection często są filtrowane, więc to dobre miejsce na indeks.
3. Magazyn: śledzenie ruchów po produkcie
Ruchy magazynowe szybko rosną w wolumenie w firmach dystrybucyjnych. Indeks na product_id w tabeli stock.move umożliwia szybkie wyszukanie wszystkich operacji dla danego towaru. Bez tego raporty śledzenia partii mogłyby stać się bardzo wolne.
4. Księgowość: filtrowanie zapisów księgowych po kontrahencie
Księgowi regularnie przeglądają zapisy dla konkretnego klienta lub dostawcy. Indeks na partner_id w account.move.line utrzymuje wydajność tych zapytań nawet przy wieloletniej historii księgowej.
5. Moduły niestandardowe: pola referencyjne dla śledzenia powiązań
W modułach niestandardowych często dodaje się pola referencyjne (kod projektu, numer dokumentu zewnętrznego). Jeśli użytkownicy będą po nich szukać, dodanie index=True utrzyma szybkość wyszukiwań w miarę przyrostu danych.
Tworzenie lub modyfikacja pola indeksowanego
W Pythonie (tworzenie modułu)
Dodanie index=True w definicji pola w module Pythona jest proste: wystarczy umieścić parametr przy deklaracji pola.
from odoo import models, fields
class ProjectTask(models.Model):
_inherit = 'project.task'
x_external_ref = fields.Char(
string='External Reference',
index=True,
help='Reference number from the external system'
)
Po dodaniu pola trzeba zaktualizować moduł poleceniem odoo-bin -u your_module_name lub przez panel Aplikacji. Odoo wykryje nowe pole i utworzy odpowiedni indeks w bazie.
Indeks na istniejącym polu można też dodać przez nadpisanie definicji pola (inherit/override), ale wymaga to ostrożności, by nie zmienić niezamierzenie innych aspektów oryginalnego pola.
W Odoo Studio
Odoo Studio pozwala nietechnicznym użytkownikom tworzyć pola w UI, ale nie daje opcji ustawienia indeksu podczas tworzenia. Pola dodane przez Studio nie mają domyślnie index=True i są zapisywane jako pola ręczne w bazie.
Jeśli pole stworzone w Studio wymaga indeksowania ze względu na wydajność, najlepszym podejściem jest przeniesienie tej zmiany do modułu Pythona i dodanie index=True tam — zadanie dla dewelopera Odoo, nie dla użytkownika Studio.
Tworzenie indeksu bezpośrednio w PostgreSQL
W pewnych sytuacjach administrator bazy może dodać indeks ręcznie, bez aktualizacji modułu, by zoptymalizować produkcyjną bazę danych.
CREATE INDEX CONCURRENTLY idx_sale_order_partner_id
ON sale_order (partner_id);
Użycie CONCURRENTLY zapobiega blokowaniu tabeli w czasie tworzenia indeksu, co ma znaczenie w środowisku produkcyjnym. Trzeba jednak zsynchronizować takie działanie z definicją modułu, bo przy kolejnej aktualizacji Odoo może nie zarządzać tym ręcznym indeksem zgodnie z oczekiwaniami.
Zalecane praktyki
Indeksuj pola, które występują w domenach wyszukiwania
Jeśli pole regularnie pojawia się w filtrach — w widokach listy, automatycznych akcjach, zadaniach okresowych czy zależnościach pól obliczanych — warto je indeksować. Najczęściej są to Many2one, pola stanu i pola referencyjne.
Kieruj się konwencjami Odoo
Najlepszym wzorcem są standardowe definicje pól w samym Odoo. Sprawdź, które kolumny w modelach takich jak sale.order, account.move czy stock.move mają indeksy — decyzje tych deweloperów wynikają z rzeczywistych wzorców użycia i obserwacji wydajności w wielu instalacjach.
Zawsze indeksuj Many2one w modelach o dużym wolumenie danych
Dla modeli, które rosną do dużej liczby rekordów (zapisów księgowych, ruchów magazynowych, linii zamówień), indeksowanie pól Many2one używanych do filtrowania to niemal obowiązek. Koszt dodatkowego indeksu przy zapisie zwykle rekompensuje znacząca poprawa czasu odpowiedzi przy odczytach.
Rozważ indeks trigramowy dla wyszukiwania tekstu
W Odoo 16+ warto użyć index='trigram' dla pól tekstowych, jeśli użytkownicy często wyszukują po fragmencie nazwy produktu, kontrahenta czy numeru dokumentu. Indeks trigramowy jest zoptymalizowany pod zapytania ILIKE i wyszukiwanie częściowego.
Sprawdź, czy indeksy są wykorzystywane
Po dodaniu indeksu warto uruchomić EXPLAIN ANALYZE dla zapytania, aby potwierdzić, że planner PostgreSQL go używa. Czasem planner wybiera skanowanie sekwencyjne — np. gdy tabela jest jeszcze za mała albo warunki zapytania nie pasują do typu indeksu.
Udokumentuj decyzje dotyczące indeksowania
W modułach niestandardowych dodaj krótki komentarz wyjaśniający, dlaczego pole ma indeks. Ułatwia to przyszłym deweloperom zrozumienie intencji i zapobiega przypadkowemu usunięciu indeksu podczas refaktoru.
Częste pułapki
Indeksowanie wszystkiego z automatu
Typowy błąd to dodawanie index=True do każdego pola na wszelki wypadek. Takie podejście zwiększa zajętość przestrzeni dyskowej i spowalnia zapisy — na tabelach o dużym natężeniu INSERT/UPDATE nadmiar indeksów może znacząco pogorszyć wydajność.
Indeksowanie małych tabel
Na małych tabelach (kilkaset wierszy) planner PostgreSQL często uznaje sekwencyjne skanowanie za szybsze niż użycie indeksu. Indeks daje realne korzyści dopiero przy skali tysięcy rekordów i więcej, więc indeksowanie rzadko używanych, małych słowników to zbędne obciążenie.
Zapomnienie o aktualizacji modułu po dodaniu index=True
Samo dopisanie index=True do definicji pola w Pythonie nie tworzy indeksu automatycznie — trzeba zaktualizować moduł (-u module_name) lub dokonać upgrade'u przez interfejs. Pominięcie tego kroku to częsta przyczyna nieoczekiwanej wolnej pracy w środowiskach testowych czy produkcyjnych.
Oczekiwanie, że zwykły B-tree przyspieszy wszystkie zapytania ILIKE
Standardowy indeks B-tree nie pomaga przy wyszukiwaniu typu ILIKE '%słowo%' z wildcardem na początku. PostgreSQL nie może wykorzystać B-tree dla prowadzącego wildcardu. W takich przypadkach użyj index='trigram' (Odoo 16+) lub rozważ pełnotekstowe wyszukiwanie.
Niebranie pod uwagę pól obliczanych przechowywanych
Pola obliczane z store=True mają fizyczną kolumnę i można je indeksować. Czasem programiści to pomijają, tracąc okazję do przyspieszenia filtrów czy raportów opartych na skalkulowanych wartościach. Jeśli takie pole pojawia się w domenach, warto rozważyć dodanie indeksu.
Podsumowanie
Atrybut index=True to drobna zmiana w definicji pola, ale ma realny wpływ na wydajność bazy danych w miarę wzrostu danych. Użyty rozsądnie, zapewnia szybkie wyszukiwanie i responsywne widoki; użyty bezmyślnie, generuje niepotrzebne obciążenie przy zapisie.
Główna zasada jest prosta: indeksuj pola, które faktycznie pojawiają się w domenach filtrów — zwłaszcza Many2one w modelach o dużym wolumenie. Kieruj się wyborem dokonanym w standardowych modułach Odoo. Unikaj nadmiarowego indeksowania małych tabel lub pól, po których nigdy się nie filtruje. Jeśli pracujesz na Odoo 16+, rozważ index='trigram' dla pól tekstowych wyszukiwanych częściowo.
Lepiej zaplanować strategię indeksowania na etapie projektowania modułu niż później diagnozować wolne zapytania na produkcji — oszczędza to czas i komplikacje.
Pracujesz nad wdrożeniem Odoo?
W Dasolo pomagamy firmom wdrażać, dostosowywać i optymalizować Odoo. Niezależnie od tego, czy tworzysz moduły niestandardowe, optymalizujesz istniejącą instancję, czy zaczynasz projekt od zera, dostarczamy praktyczną wiedzę techniczną i wsparcie wdrożeniowe.
Jeśli masz problemy ze zbyt wolnymi zapytaniami, skomplikowanymi modyfikacjami lub potrzebujesz porady dotyczącej dobrych praktyk w rozwoju Odoo — chętnie pomożemy. Skontaktuj się z zespołem Dasolo i opowiedz nam, nad czym pracujesz.