8

SQLAlchemyでマップされたオブジェクトの1つの複数のテキストフィールドを全文検索できるようにしたいと思います。また、マップされたオブジェクトが外部キーとトランザクションをサポートするようにしたいと思います。

MySQLを使用して全文検索を実行する予定です。ただし、MySQLはMyISAMテーブルでのみ全文検索を実行できることを理解しています。これは、トランザクションと外部キーをサポートしていません。

私の目的を達成するために、2つのテーブルを作成する予定です。私のコードは次のようになります。

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    description = Column(Text)

users_myisam = Table('users_myisam', Base.metadata,
                     Column('id', Integer),
                     Column('name', String(50)),
                     Column('description', Text),
                     mysql_engine='MyISAM')

conn = Base.metadata.bind.connect()
conn.execute("CREATE FULLTEXT INDEX idx_users_ftxt \
              on users_myisam (name, description)")

次に、検索するためにこれを実行します:

q = 'monkey'
ft_search = users_myisam.select("MATCH (name,description) AGAINST ('%s')" % q)
result = ft_search.execute()
for row in result: print row

これはうまくいくようですが、いくつか質問があります。

  1. 問題を解決するために2つのテーブルを作成するという私のアプローチは合理的ですか?これを行うための標準/より良い/よりクリーンな方法はありますか?

  2. フルテキストインデックスを作成するSQLAlchemyの方法はありますか、それとも上記のように「CREATE FULLTEXTINDEX ...」を直接実行するのが最善ですか?

  3. 検索/クエリとの照合でSQLインジェクションの問題が発生しているようです。これを修正するには、「SQLAlchemyの方法」を選択するにはどうすればよいですか?

  4. users_myisam select / matchをユーザーテーブルに直接結合して実際のユーザーインスタンスを返すクリーンな方法はありますか?これが私が本当に望んでいることなのでしょうか?

  5. users_myisamテーブルをマップされたオブジェクトのuserテーブルと同期させるために、UserクラスでMapperExtensionを使用し、before_insert、before_update、およびbefore_deleteメソッドを設定してusers_myisamテーブルを適切に更新するのは理にかなっていますか。これを達成するためのより良い方法はありますか?

ありがとう、マイケル

4

1 に答える 1

14

問題を解決するために 2 つのテーブルを作成するという私のアプローチは合理的ですか? これを行うための標準/より良い/よりクリーンな方法はありますか?

トランザクションと制約を重視する開発者は、そもそも Postgresql を使用する傾向があるため、この使用例が試みられたことはこれまでありませんでした。特定のシナリオでは不可能な場合があることを理解しています。

フルテキスト インデックスを作成する SQLAlchemy の方法はありますか、それとも上記のように「CREATE FULLTEXT INDEX ...」を直接実行するのが最善ですか?

conn.execute() は問題ありませんが、もう少し統合したい場合は、DDL() コンストラクトを使用して、http://docs.sqlalchemy.org/en/rel_0_8/core/schema.html ?highlight=ddl# を読んでください。詳細については、カスタマイズ-ddl

クエリに対する検索/一致で SQL インジェクションの問題があるようです。これを修正するには、「SQLAlchemy の方法」を選択するにはどうすればよいですか?

: このレシピは、同時に複数の列に対してのみ使用できます。列が 1 つしかない場合は、より簡単に match() 演算子を使用しくださいMATCH

最も基本的には、 text() コンストラクトを使用できます。

from sqlalchemy import text, bindparam

users_myisam.select(
  text("MATCH (name,description) AGAINST (:value)", 
       bindparams=[bindparam('value', q)])
)

より包括的には、カスタム コンストラクトを定義できます。

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import ClauseElement
from sqlalchemy import literal

class Match(ClauseElement):
    def __init__(self, columns, value):
        self.columns = columns
        self.value = literal(value)

@compiles(Match)
def _match(element, compiler, **kw):
    return "MATCH (%s) AGAINST (%s)" % (
               ", ".join(compiler.process(c, **kw) for c in element.columns),
               compiler.process(element.value)
             )

my_table.select(Match([my_table.c.a, my_table.c.b], "some value"))

ドキュメント:

http://docs.sqlalchemy.org/en/rel_0_8/core/compiler.html

users_myisam select/match を直接ユーザー テーブルに結合し、実際のユーザー インスタンスを返すクリーンな方法はありますか?

おそらく UserMyISAM クラスを作成し、それを User と同じようにマップし、次に relationship() を使用して 2 つのクラスをリンクすると、次のような単純な操作が可能になります。

query(User).join(User.search_table).\
           filter(Match([UserSearch.x, UserSearch.y], "some value"))

users_myisam テーブルをマップされたオブジェクト ユーザー テーブルと同期させるために、User クラスで MapperExtension を使用し、before_insert、before_update、および before_delete メソッドを設定して users_myisam テーブルを適切に更新することは理にかなっていますか?これを達成するためのより良い方法はありますか?

MapperExtensions は非推奨であるため、少なくともイベント APIを使用する必要があります。ほとんどの場合、フラッシュ プロセスの外部でオブジェクト ミューテーションを適用してみます。この場合、 User のコンストラクター、または代わりにinit eventと、 User のターゲット属性の値を受け取り、それらの値を にコピーする基本的な@validatesUser.search_tableデコレーターを使用します。

全体として、SQLAlchemy を別の情報源 (Oreilly の本など) から学んでいる場合、それは何年も前から時代遅れになっているため、現在のオンライン ドキュメントに集中することになります。

于 2013-02-24T18:03:51.023 に答える