Собственные алгоритмы сортировки SQLAlchemy при использовании индексов SQL - PullRequest
3 голосов
/ 18 апреля 2010

Можно ли написать пользовательские функции сопоставления с индексами в SQLAlchemy? Например, SQLite позволяет указать функцию сортировки на уровне C как sqlite3_create_collation().

Реализация некоторых алгоритмов сопоставления Unicode была предоставлена ​​Джеймсом Таубером здесь , который, например, сортирует все "а" близко друг к другу, независимо от того, имеют ли они акценты или нет. *

Другие примеры того, почему это может быть полезно, для разных порядков алфавита (кроме английского) и сортировки числовых значений (сортировка 10 после 9 вместо порядка кодовых точек).

Возможно ли это в SQLAlchemy? Если нет, поддерживается ли он модулями pysqlite3 или MySQLdb или для каких-либо других модулей базы данных SQL, поддерживаемых python в этом отношении?

Любая информация будет принята с благодарностью.

Ответы [ 2 ]

1 голос
/ 20 апреля 2010

Ниже приведен пример, демонстрирующий алгоритм сопоставления юникода для sqlite:

from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from pyuca import Collator

metadata = MetaData()
Base = declarative_base(metadata=metadata)

class Item(Base):
    __tablename__ = 'Item'
    id = Column(Integer, primary_key=True)
    value = Column(String, nullable=False)

collator = Collator('allkeys.txt')

def collate_unicode(value1, value2):
    return cmp(collator.sort_key(value1), collator.sort_key(value2))

engine = create_engine('sqlite://')
engine.raw_connection().create_collation('unicode', collate_unicode)
metadata.create_all(engine)
session = sessionmaker(engine)()

for word in [u"ĉambr", u"ĉar", u"car'", u"carin'", u"ĉe", u"ĉef'",
             u"centjar'", u"centr'", u"cerb'", u"cert'", u"ĉes'", u"ceter'"]:
    item = Item(value=word)
    session.add(item)
    session.commit()

for item in session.query(Item).order_by(collate(Item.value, 'unicode')):
    print item.value
0 голосов
/ 20 апреля 2010

Я немного изменил ответ Дениса Откидача, поэтому я добавлю свои изменения в вики сообщества на тот случай, если кому-то будет интересно:

# -*- coding: utf-8 -*-
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import types
from pyuca import Collator

class MyUnicode(types.TypeDecorator):
    impl = types.Unicode
    def get_col_spec(self):
        # Return a new Unicode type sorted with 
        # the `mycollation` function
        return 'Unicode COLLATE mycollation'

# Create the collator (sorting) function/instance
collator = Collator('allkeys.txt')
def mycollation(value1, value2):
    if False:
        # Use pyuca for sorting
        return cmp(collator.sort_key(value1), 
                   collator.sort_key(value2))
    else:
        # Normalize to lowercased combining characters for sorting
        import unicodedata
        return cmp(unicodedata.normalize('NFD', unicode(value1)).lower(),
                   unicodedata.normalize('NFD', unicode(value2)).lower())

# Create a new metadata/base/table
metadata = MetaData()
Base = declarative_base(metadata=metadata)
class Item(Base):
    __tablename__ = 'CollatedTable'
    id = Column(Integer, primary_key=True)
    # (Note the `unique=True` in the next line so that an index 
    #  is created, therefore stored in collated order for faster SELECTs)
    value = Column(MyUnicode(), nullable=False, unique=True)

# Create a new database connection
engine = create_engine('sqlite://')
engine.echo = True # Print the SQL
engine.raw_connection().create_collation('mycollation', mycollation)
metadata.create_all(engine)
session = sessionmaker(engine)()

# Add some test data
for word in [u"ĉambr", u"ĉar", u"car'", u"carin'", u"ĉe", u"ĉef'",
             u"centjar'", u"centr'", u"cerb'", u"cert'", u"ĉes'", u"ceter'",

             u"zimble", u'bumble', 
             u'apple', u'ápple', u'ãpple',
             u'đjango', u'django']:
    item = Item(value=word)
    session.add(item)
session.commit()

for item in session.query(Item).order_by(Item.value): # collate(Item.value, 'mycollation')
    print item.value
...