Projet

Général

Profil

0001-sql-add-fts-column-to-users-table-8973.patch

Frédéric Péters, 13 novembre 2015 14:33

Télécharger (9,88 ko)

Voir les différences:

Subject: [PATCH] sql: add fts column to users table (#8973)

 tests/test_sql.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 wcs/sql.py        | 87 ++++++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 143 insertions(+), 27 deletions(-)
tests/test_sql.py
405 405
    assert sql.SqlUser.get(12345, ignore_errors=True) is None
406 406

  
407 407
@postgresql
408
def test_user_formdef():
409
    sql.SqlUser.wipe()
410

  
411
    from wcs.admin.settings import UserFieldsFormDef
412
    formdef = UserFieldsFormDef(pub)
413
    formdef.fields = [fields.StringField(id='3', label='test', type='string')]
414
    formdef.store()
415

  
416
    user = sql.SqlUser()
417
    user.name = 'Pierre'
418
    user.form_data = {'3': 'Papier'}
419
    user.store()
420

  
421
    assert sql.SqlUser.get(user.id, ignore_errors=True).form_data['3'] == 'Papier'
422

  
423
    del pub.cfg['users']['formdef']
424
    pub.write_cfg()
425

  
426
@postgresql
408 427
def test_get_users_with_role():
409 428
    sql.SqlUser.wipe()
410 429

  
......
437 456
    assert len(sql.SqlUser.get_users_with_name_identifier('foo')) == 1
438 457
    assert sql.SqlUser.get_users_with_name_identifier('foo')[0].name == 'Pierre'
439 458

  
459
@postgresql
460
def test_get_users_fts():
461
    sql.SqlUser.wipe()
462

  
463
    user = sql.SqlUser()
464
    user.name = 'Pierre'
465
    user.name_identifiers = ['foo']
466
    user.store()
467
    user_id = user.id
468

  
469
    user = sql.SqlUser()
470
    user.name = 'Papier'
471
    user.store()
472

  
473
    assert len(sql.SqlUser.get_ids_from_query('pierre')) == 1
474
    assert sql.SqlUser.get(sql.SqlUser.get_ids_from_query('pierre')[0]).id == user_id
475

  
476
@postgresql
477
def test_get_users_formdef_fts():
478
    sql.SqlUser.wipe()
479

  
480
    from wcs.admin.settings import UserFieldsFormDef
481
    formdef = UserFieldsFormDef(pub)
482
    formdef.fields = [fields.StringField(id='3', label='test', type='string')]
483
    formdef.store()
484

  
485
    user = sql.SqlUser()
486
    user.name = 'Pierre'
487
    user.form_data = {'3': 'Papier'}
488
    user.store()
489
    user_id = user.id
490

  
491
    assert len(sql.SqlUser.get_ids_from_query('pierre papier')) == 1
492
    assert sql.SqlUser.get(sql.SqlUser.get_ids_from_query('pierre papier')[0]).id == user_id
493

  
494
    assert len(sql.SqlUser.get_ids_from_query('papier pierre')) == 1
495
    assert sql.SqlUser.get(sql.SqlUser.get_ids_from_query('papier pierre')[0]).id == user_id
496

  
497
    del pub.cfg['users']['formdef']
498
    pub.write_cfg()
440 499

  
441 500
@postgresql
442 501
def test_urlname_change():
......
846 905
    conn.commit()
847 906
    cur.close()
848 907

  
908
@postgresql
909
def test_migration_12_users_fts():
910
    conn, cur = sql.get_connection_and_cursor()
911
    cur.execute('UPDATE wcs_meta SET value = 11 WHERE key = %s', ('sql_level',))
912

  
913
    sql.SqlUser.wipe()
914

  
915
    user = sql.SqlUser()
916
    user.name = 'Pierre'
917
    user.store()
918

  
919
    # remove the fts column
920
    cur.execute('ALTER TABLE users DROP COLUMN fts')
921
    assert not column_exists_in_table(cur, 'users', 'fts')
922
    sql.migrate()
923

  
924
    assert column_exists_in_table(cur, 'users', 'fts')
925
    assert migration_level(cur) >= 12
926

  
927
    # make sure the fts is filled after the migration
928
    assert len(sql.SqlUser.get_ids_from_query('pierre')) == 1
929

  
930
    conn.commit()
931
    cur.close()
849 932

  
850 933
def drop_formdef_tables():
851 934
    conn, cur = sql.get_connection_and_cursor()
wcs/sql.py
416 416

  
417 417
    needed_fields = set(['id', 'name', 'email', 'roles', 'is_admin',
418 418
        'anonymous', 'name_identifiers',
419
        'lasso_dump', 'last_seen'])
419
        'lasso_dump', 'last_seen', 'fts'])
420 420

  
421 421
    from admin.settings import UserFieldsFormDef
422 422
    formdef = UserFieldsFormDef()
......
442 442
                cur.execute('''ALTER TABLE %s ADD COLUMN %s bytea''' % (
443 443
                                        table_name, 'f%s_structured' % field.id))
444 444

  
445
    # migrations
446
    if not 'fts' in existing_fields:
447
        # full text search
448
        cur.execute('''ALTER TABLE %s ADD COLUMN fts tsvector''' % table_name)
449
        cur.execute('''CREATE INDEX %s_fts ON %s USING gin(fts)''' % (
450
                                table_name, table_name))
451

  
445 452
    # delete obsolete fields
446 453
    for field in (existing_fields - needed_fields):
447 454
        cur.execute('''ALTER TABLE %s DROP COLUMN %s''' % (table_name, field))
......
726 733
    get_with_indexed_value = classmethod(get_with_indexed_value)
727 734

  
728 735
    @guard_postgres
736
    def get_ids_from_query(cls, query):
737
        conn, cur = get_connection_and_cursor()
738

  
739
        sql_statement = '''SELECT id FROM %s
740
                            WHERE fts @@ plainto_tsquery(%%(value)s)''' % cls._table_name
741
        cur.execute(sql_statement, {'value': query})
742
        all_ids = [x[0] for x in cur.fetchall()]
743
        cur.close()
744
        return all_ids
745
    get_ids_from_query = classmethod(get_ids_from_query)
746

  
747
    @guard_postgres
729 748
    def get(cls, id, ignore_errors=False, ignore_migration=False):
730 749
        if id is None:
731 750
            if ignore_errors:
......
1246 1265
    get_ids_with_indexed_value = classmethod(get_ids_with_indexed_value)
1247 1266

  
1248 1267
    @guard_postgres
1249
    def get_ids_from_query(cls, query):
1250
        conn, cur = get_connection_and_cursor()
1251

  
1252
        sql_statement = '''SELECT id FROM %s
1253
                            WHERE fts @@ plainto_tsquery(%%(value)s)''' % cls._table_name
1254
        cur.execute(sql_statement, {'value': query})
1255
        all_ids = [x[0] for x in cur.fetchall()]
1256
        cur.close()
1257
        return all_ids
1258
    get_ids_from_query = classmethod(get_ids_from_query)
1259

  
1260
    @guard_postgres
1261 1268
    def fix_sequences(cls):
1262 1269
        conn, cur = get_connection_and_cursor()
1263 1270

  
......
1331 1338
        if self.last_seen:
1332 1339
            sql_dict['last_seen'] = datetime.datetime.fromtimestamp(self.last_seen),
1333 1340

  
1334
        sql_dict.update(self.get_sql_dict_from_data(self.form_data, self.get_formdef()))
1341
        user_formdef = self.get_formdef()
1342
        sql_dict.update(self.get_sql_dict_from_data(self.form_data, user_formdef))
1335 1343

  
1336 1344
        conn, cur = get_connection_and_cursor()
1337 1345
        if not self.id:
......
1359 1367
                                       ', '.join(['%%(%s)s' % x for x in column_names]))
1360 1368
                cur.execute(sql_statement, sql_dict)
1361 1369

  
1370
        fts_strings = []
1371
        if self.name:
1372
            fts_strings.append(self.name)
1373
        if self.email:
1374
            fts_strings.append(self.email)
1375
        if user_formdef and user_formdef.fields:
1376
            for field in user_formdef.fields:
1377
                if not self.form_data.get(field.id):
1378
                    continue
1379
                value = None
1380
                if field.key in ('string', 'text', 'email'):
1381
                    value = self.form_data.get(field.id)
1382
                elif field.key in ('item', 'items'):
1383
                    value = self.form_data.get('%s_display' % field.id)
1384
                if value:
1385
                    if isinstance(value, basestring):
1386
                        fts_strings.append(value)
1387
                    elif type(value) in (tuple, list):
1388
                        fts_strings.extend(value)
1389
        sql_statement = '''UPDATE %s SET fts = to_tsvector( %%(fts)s)
1390
                            WHERE id = %%(id)s''' % self._table_name
1391
        cur.execute(sql_statement, {'id': self.id, 'fts': ' '.join(fts_strings)})
1392

  
1362 1393
        conn.commit()
1363 1394
        cur.close()
1364 1395

  
......
1694 1725
    return result
1695 1726

  
1696 1727

  
1697
SQL_LEVEL = 11
1728
SQL_LEVEL = 12
1698 1729

  
1699 1730
def migrate_global_views(conn, cur):
1700 1731
    cur.execute('''SELECT COUNT(*) FROM information_schema.tables
......
1728 1759
        raise RuntimeError()
1729 1760
    if sql_level < 1: # 1: introduction of tracking_code table
1730 1761
        do_tracking_code_table()
1731
    if sql_level < 2: # 2: introduction of formdef_id in views
1762
    if sql_level < 11:
1763
        # 2: introduction of formdef_id in views
1764
        # 5: add concerned_roles_array, is_at_endpoint and fts to views
1765
        # 7: add backoffice_submission to tables and views
1766
        # 8: add submission_context to tables
1767
        # 9: add last_update_time to views
1768
        # 10: add submission_channel to tables
1769
        # 11: add formdef_name and user_name to views
1732 1770
        migrate_views(conn, cur)
1733
    if sql_level < 4:
1771
    if sql_level < 12:
1734 1772
        # 3: introduction of _structured for user fields
1735 1773
        # 4: removal of identification_token
1774
        # 12: (first part) add fts to users
1736 1775
        do_user_table()
1737
    if sql_level < 5:
1738
        # 5: add concerned_roles_array, is_at_endpoint and fts to views
1739
        migrate_views(conn, cur)
1740 1776
    if sql_level < 6:
1741 1777
        # 6: add actions_roles_array to tables and views
1742 1778
        from wcs.formdef import FormDef
1743 1779
        migrate_views(conn, cur)
1744 1780
        for formdef in FormDef.select():
1745 1781
            formdef.data_class().rebuild_security()
1746
    if sql_level < 11:
1747
        # 7: add backoffice_submission to tables and views
1748
        # 8: add submission_context to tables
1749
        # 9: add last_update_time to views
1750
        # 10: add submission_channel to tables
1751
        # 11: add formdef_name and user_name to views
1752
        migrate_views(conn, cur)
1782
    if sql_level < 12:
1783
        # 12: (second part), store fts in existing rows
1784
        for user_id in SqlUser.keys():
1785
            SqlUser.get(user_id).store()
1753 1786

  
1754 1787
    cur.execute('''UPDATE wcs_meta SET value = %s WHERE key = %s''', (
1755 1788
        str(SQL_LEVEL), 'sql_level'))
1756
-