Skip to content
This repository was archived by the owner on Sep 30, 2019. It is now read-only.

Commit 9f19e2c

Browse files
committed
added migrations.rst
1 parent c04d950 commit 9f19e2c

File tree

4 files changed

+251
-22
lines changed

4 files changed

+251
-22
lines changed

docs/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Welcome to django-pg-fts's documentation!
77
=========================================
88

9-
Implementation PostgeSQL for Full Text Search for Django 1.7, taking advantage of new features Migrations and Custom Lookups.
9+
Implementation PostgeSQL for Full Text Search for django 1.7, taking advantage of new features Migrations and Custom Lookups.
1010

1111
Features:
1212
---------
@@ -31,6 +31,7 @@ Contents:
3131

3232
install
3333
tutorial
34+
migrations
3435
pg_fts
3536

3637

docs/migrations.rst

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
Migrations
2+
==========
3+
4+
5+
.. important::
6+
7+
If you're not familiar with django Migrations, please check `Migrations Documentation <https://docs.djangoproject.com/en/1.7/topics/migrations/>`_ 1st.
8+
9+
10+
.. important::
11+
12+
The order of Migrations.operations matters, you can only apply a index to a field if a field exists, if you delete a model/field you should remove any operation first before removing model/field.
13+
14+
15+
Creating
16+
--------
17+
18+
Trigger
19+
*******
20+
21+
For creating of trigger is provided :class:`~pg_fts.migrations.CreateFTSTriggerOperation`.
22+
23+
``name``
24+
25+
.. attribute:: CreateFTSTriggerOperation.name
26+
27+
Name of the model
28+
29+
``fts_vector``
30+
31+
.. attribute:: CreateFTSTriggerOperation.fts_vector
32+
33+
Name of the :class:`~pg_fts.fields.TSVectorField`
34+
35+
How it works
36+
++++++++++++
37+
38+
The trigger only updates the :class:`~pg_fts.fields.TSVectorField` if data is changed in the fields that is indexing, with it's weight (default is 'D') and language.
39+
40+
Example for this model::
41+
42+
class Article(models.Model):
43+
title = models.CharField(max_length=255)
44+
article = models.TextField()
45+
fts = TSVectorField(
46+
(('title', 'A'), 'article'),
47+
dictionary='portuguese'
48+
)
49+
50+
In Migrations::
51+
52+
class Migration(migrations.Migration)
53+
dependencies = [
54+
('article', '00XX_create_fts_field'),
55+
]
56+
57+
operations = [
58+
CreateFTSTriggerOperation(
59+
name='Article',
60+
fts_vector='fts',
61+
),
62+
]
63+
64+
Will create this trigger:
65+
66+
.. code-block:: sql
67+
68+
CREATE FUNCTION article_article_fts_update() RETURNS TRIGGER AS $$
69+
BEGIN
70+
IF TG_OP = 'INSERT' THEN
71+
new.fts = setweight(to_tsvector('portuguese', COALESCE(NEW.title, '')), 'A') || setweight(to_tsvector('portuguese', COALESCE(NEW.article, '')), 'D');
72+
END IF;
73+
IF TG_OP = 'UPDATE' THEN
74+
IF NEW.title <> OLD.title OR NEW.article <> OLD.article THEN
75+
new.fts = setweight(to_tsvector('portuguese', COALESCE(NEW.title, '')), 'A') || setweight(to_tsvector('portuguese', COALESCE(NEW.article, '')), 'D');
76+
END IF;
77+
END IF;
78+
RETURN NEW;
79+
END;
80+
$$ LANGUAGE 'plpgsql';
81+
CREATE TRIGGER article_article_fts_update BEFORE INSERT OR UPDATE ON article_article
82+
FOR EACH ROW EXECUTE PROCEDURE article_article_fts_update();
83+
84+
85+
.. important::
86+
87+
Trigger will only work for future changes, for existing data use :class:`~pg_fts.migrations.UpdateVectorOperation`.
88+
89+
Index
90+
*****
91+
92+
For creating of indexes is provided :class:`~pg_fts.migrations.CreateFTSIndexOperation`.
93+
94+
``name``
95+
96+
.. attribute:: CreateFTSIndexOperation.name
97+
98+
Name of the model
99+
100+
``fts_vector``
101+
102+
.. attribute:: CreateFTSIndexOperation.fts_vector
103+
104+
Name of the :class:`~pg_fts.fields.TSVectorField`
105+
106+
107+
``index``
108+
109+
.. attribute:: CreateFTSIndexOperation.index
110+
111+
Options:
112+
113+
- ``gin`` GIN (Generalized Inverted Index)-based index. Faster to build, slower to lookup.
114+
115+
- ``gist`` GiST (Generalized Search Tree)-based index. Faster to lookup, slower to build.
116+
117+
For information about the ``gin`` and ``gist`` indexes types consult :pg_docs:`PostgreSQL documentation 12.9. GiST and GIN Index Types <textsearch-indexes.html>`
118+
119+
120+
Migrating from existing application
121+
-----------------------------------
122+
123+
For existing application with data is provided :class:`~pg_fts.migrations.UpdateVectorOperation`, this will update the vector.
124+
125+
``name``
126+
127+
.. attribute:: UpdateVectorOperation.name
128+
129+
Name of the model
130+
131+
``fts_vector``
132+
133+
.. attribute:: UpdateVectorOperation.fts_vector
134+
135+
Name of the :class:`~pg_fts.fields.TSVectorField`
136+
137+
138+
Changing and Removing
139+
---------------------
140+
141+
Changing Index
142+
**************
143+
144+
If you have a existing index created by :class:`~pg_fts.migrations.CreateFTSIndexOperation` of type ``gin`` to migrate for ``gist`` you have to 1st remove the existing index with :class:`~pg_fts.migrations.DeleteFTSIndexOperation` and create a of type ``gist`` with :class:`~pg_fts.migrations.CreateFTSIndexOperation`.
145+
146+
Example::
147+
148+
class Migration(migrations.Migration)
149+
dependencies = [
150+
('article', '0003_fts_create_index_trigger'),
151+
]
152+
153+
operations = [
154+
DeleteFTSIndexOperation(
155+
name='Article',
156+
fts_vector='fts_index',
157+
index='gin'
158+
),
159+
CreateFTSIndexOperation(
160+
name='Article',
161+
fts_vector='fts_index',
162+
index='gist'
163+
),
164+
]
165+
166+
Alterations on :class:`~pg_fts.fields.TSVectorField`
167+
****************************************************
168+
169+
If you change :class:`~pg_fts.fields.TSVectorField` is fields, ranks or dictionary you have to:
170+
171+
1. remove the trigger with :class:`~pg_fts.migrations.DeleteFTSTriggerOperation` and only after you can create
172+
173+
2. a new trigger with :class:`~pg_fts.migrations.CreateFTSTriggerOperation`.
174+
175+
For updating :class:`~pg_fts.fields.TSVectorField` run :class:`~pg_fts.migrations.UpdateVectorOperation`.
176+
177+
.. hint::
178+
179+
If the fields are the same (fields and rank) but you are updating to multiple dictionaries, for efficiency, keep the previous dictionary as default, as the lexemes and weight will be the same in :class:`~pg_fts.fields.TSVectorField`.
180+
There is no need to run :class:`~pg_fts.migrations.UpdateVectorOperation`
181+
182+
Removing Index
183+
**************
184+
185+
For removing the index is provided :class:`~pg_fts.migrations.DeleteFTSIndexOperation`.
186+
187+
``name``
188+
189+
.. attribute:: DeleteFTSIndexOperation.name
190+
191+
Name of the model
192+
193+
``fts_vector``
194+
195+
.. attribute:: DeleteFTSIndexOperation.fts_vector
196+
197+
Name of the :class:`~pg_fts.fields.TSVectorField`
198+
199+
The previous index type, important for regressions.
200+
201+
``index``
202+
203+
.. attribute:: CreateFTSIndexOperation.index
204+
205+
206+
Removing Trigger
207+
****************
208+
209+
For removing the index is provided :class:`~pg_fts.migrations.DeleteFTSTriggerOperation`.
210+
211+
``name``
212+
213+
.. attribute:: DeleteFTSTriggerOperation.name
214+
215+
Name of the model
216+
217+
``fts_vector``
218+
219+
.. attribute:: DeleteFTSTriggerOperation.fts_vector
220+
221+
Name of the :class:`~pg_fts.fields.TSVectorField`

docs/pg_fts.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ pg_fts.fields module
1313
pg_fts.aggregates module
1414
------------------------
1515

16-
.. automodule:: pg_fts.aggregates
16+
.. note::
17+
18+
Deprecated use :mod:`pg_fts.ranks` instead.
19+
20+
pg_fts.ranks module
21+
------------------------
22+
23+
.. automodule:: pg_fts.ranks
1724
:members:
1825

1926

docs/tutorial.rst

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Create a new project like ``fooproject`` and ``article`` app::
1414

1515
**Add ``pg_fts`` To ``INSTALLED_APPS``**
1616

17-
As with most Django applications, you should add ``pg_fts`` to the ``INSTALLED_APPS`` in your ``settings.py`` file::
17+
As with most django applications, you should add ``pg_fts`` to the ``INSTALLED_APPS`` in your ``settings.py`` file::
1818

1919
INSTALLED_APPS = (
2020
'django.contrib.auth',
@@ -173,26 +173,26 @@ With ``python manage.py shell``::
173173
>>> from testapp.models import Article
174174
>>> Article.objects.create(title='PHP', article='what a pain, the worst of c, c++, perl all mixed in one stupid thing')
175175
>>> Article.objects.create(title='Python', article='is awesome')
176-
>>> Article.objects.create(title='Django', article='is awesome, made in python, multiple databases support, it has a ORM, class based views, template layer')
176+
>>> Article.objects.create(title='django', article='is awesome, made in python, multiple databases support, it has a ORM, class based views, template layer')
177177
>>> Article.objects.create(title='Wordpress', article="what a pain, made in PHP, it's ok if you just add a template and some plugins")
178178
>>> Article.objects.create(title='Javascript', article='A functional language, with c syntax. The braces nightmare')
179179
>>> Article.objects.filter(fts_index__search='django')
180-
[<Article: Django>]
180+
[<Article: django>]
181181
>>> Article.objects.filter(fts_index__search='Python')
182-
[<Article: Python>, <Article: Django>]
182+
[<Article: Python>, <Article: django>]
183183
>>> Article.objects.filter(fts_index__search='templates')
184-
[<Article: Wordpress>, <Article: Django>]
184+
[<Article: Wordpress>, <Article: django>]
185185
# postgress & and
186186
search = Article.objects.filter(fts_index__search='templates awesome')
187187
>>> print(search.query)
188188
SELECT "article_article"."id", "article_article"."title", "article_article"."article", "article_article"."fts_index" FROM "article_article" WHERE "article_article"."fts_index" @@ to_tsquery('portuguese', templates & awesome)
189189
print(search)
190-
[<Article: Django>] # only django has template language AND is awesome
190+
[<Article: django>] # only django has template language AND is awesome
191191
isearch = Article.objects.filter(fts_index__isearch='templates awesome')
192192
>>> print(isearch.query)
193193
SELECT "article_article"."id", "article_article"."title", "article_article"."article", "article_article"."fts_index" FROM "article_article" WHERE "article_article"."fts_index" @@ to_tsquery('portuguese', templates | awesome)
194194
print(isearch)
195-
[<Article: Python>, <Article: Wordpress>, <Article: Django>]
195+
[<Article: Python>, <Article: Wordpress>, <Article: django>]
196196
# wordpress oh no and in 2nd position, let's rank the results
197197

198198
Ranking results
@@ -205,16 +205,16 @@ For this lets use :class:`~pg_fts.ranks.FTSRank`, :class:`~pg_fts.ranks.FTSRankC
205205
>>> from pg_fts.ranks import FTSRank, FTSRankCd
206206
>>> ranks = Article.objects.annotate(rank=FTSRank(fts_index__isearch='templates awesome')).order_by('-rank')
207207
>>> ranks
208-
[<Article: Django>, <Article: Python>, <Article: Wordpress>]
208+
[<Article: django>, <Article: Python>, <Article: Wordpress>]
209209
# that's better, wordpress has templates, but it's not awesome, but let's check ranks
210210
>>> [(r.title, r.rank) for r in ranks]
211-
[('Django', 0.0607927), ('Python', 0.0303964), ('Wordpress', 0.0303964)]
211+
[('django', 0.0607927), ('Python', 0.0303964), ('Wordpress', 0.0303964)]
212212
# lucky for python appear before wordpress, let's normalize the results
213213
>>> ranks_cd = Article.objects.annotate(rank=FTSRankCd(fts_index__isearch='awesome templates', normalization=[16|32])).order_by('-rank')
214214
>>> [(r.title, r.rank) for r in ranks_cd]
215-
[('Python', 0.047619), ('Django', 0.0457674), ('Wordpress', 0.0234196)]
215+
[('Python', 0.047619), ('django', 0.0457674), ('Wordpress', 0.0234196)]
216216

217-
Python and Django are awesome, check the postgres documentation for more about normalization
217+
Python and django are awesome, check the postgres documentation for more about normalization
218218

219219
Multiple dictionary example
220220
---------------------------
@@ -315,13 +315,13 @@ For Portuguese search::
315315

316316
>>> ArticleMulti.objects.create(title='PHP', article='what a pain, the worst of c, c++, perl all mixed in one stupid thing', dictionary='english')
317317
>>> ArticleMulti.objects.create(title='Python', article='is awesome', dictionary='english')
318-
>>> ArticleMulti.objects.create(title='Django', article='is awesome, made in python', dictionary='english')
318+
>>> ArticleMulti.objects.create(title='django', article='is awesome, made in python', dictionary='english')
319319
>>> ArticleMulti.objects.create(title='Wordpress', article="what a pain, made in PHP, it's ok if you just add a template and some plugins")
320320
>>> ArticleMulti.objects.create(title='Javascript', article='A functional dictionary, with c syntax. The braces nightmare', dictionary='english')
321321
## Portuguese
322322
>>> ArticleMulti.objects.create(title='PHP', article='que dor, o pior do c, c++ e perl tudo junto para ser a coisa mais estupida', dictionary='portuguese')
323323
>>> ArticleMulti.objects.create(title='Python', article='é Brutal', dictionary='portuguese')
324-
>>> ArticleMulti.objects.create(title='Django', article='é Altamente, feito em python', dictionary='portuguese')
324+
>>> ArticleMulti.objects.create(title='django', article='é Altamente, feito em python', dictionary='portuguese')
325325
>>> ArticleMulti.objects.create(title='Wordpress', article="que dor, feito em PHP, não é mau para quem usa os templates e plugins")
326326
>>> ArticleMulti.objects.create(title='Javascript', article='Uma linguagem funcional, mas tem sintaxe c para confundir. O pesadelo das chavetas', dictionary='portuguese')
327327
>>> django_pt = ArticleMulti.objects.filter(fts_index__portuguese__search='django', dictionary='portuguese')
@@ -360,8 +360,8 @@ reverse migration to 0001 so does not include ArticleMulti::
360360
Delete the 0002 migration, remove ArticleMulti from models.py and add / change Article to::
361361

362362
class Article(models.Model):
363-
title = models.CharField(max_length=255)
364-
article = models.TextField()
363+
title = models.CharField(max_length=255)
364+
article = models.TextField()
365365

366366
dictionary = models.CharField(
367367
max_length=15,
@@ -377,11 +377,11 @@ Delete the 0002 migration, remove ArticleMulti from models.py and add / change A
377377
def __str__(self):
378378
return self.title
379379

380-
Let Django find the model alterations for us::
380+
Let django find the model alterations for us::
381381

382382
python manage.py makemigrations article
383383

384-
But we have to edit the migrations 0002 file before applying and add to operations :class:`~pg_fts.migrations.DeleteFTSTriggerOperation` and :class:`~pg_fts.migrations.DeleteFTSIndexOperation` **before** Django auto migrations, and **at the end** of operations the :class:`~pg_fts.migrations.CreateFTSIndexOperation` and :class:`~pg_fts.migrations.CreateFTSTriggerOperation`.
384+
But we have to edit the migrations 0002 file before applying and add to operations :class:`~pg_fts.migrations.DeleteFTSTriggerOperation` and :class:`~pg_fts.migrations.DeleteFTSIndexOperation` **before** django auto migrations, and **at the end** of operations the :class:`~pg_fts.migrations.CreateFTSIndexOperation` and :class:`~pg_fts.migrations.CreateFTSTriggerOperation`.
385385

386386
The migrations 0002 file should be like this::
387387

@@ -414,7 +414,7 @@ The migrations 0002 file should be like this::
414414
fts_vector='fts_index',
415415
index='gin'
416416
),
417-
# the Django created changes
417+
# the django created changes
418418
migrations.AddField(
419419
model_name='article',
420420
name='dictionary',
@@ -444,8 +444,8 @@ The migrations 0002 file should be like this::
444444

445445
Pay special attention to the order of creation and deleting.
446446

447-
You can only apply :class:`~pg_fts.migrations.CreateFTSIndexOperation` and :class:`~pg_fts.migrations.CreateFTSTriggerOperation` after Django created operations.
447+
You can only apply :class:`~pg_fts.migrations.CreateFTSIndexOperation` and :class:`~pg_fts.migrations.CreateFTSTriggerOperation` after django created operations.
448448

449-
The :class:`~pg_fts.migrations.DeleteFTSTriggerOperation` and :class:`~pg_fts.migrations.DeleteFTSIndexOperation` before Django removing/altering operations
449+
The :class:`~pg_fts.migrations.DeleteFTSTriggerOperation` and :class:`~pg_fts.migrations.DeleteFTSIndexOperation` before django removing/altering operations
450450

451451
Not to forget **USE AT YOUR OWN RISK**

0 commit comments

Comments
 (0)