1

Django アプリケーションの場合、データベースに一致に関連するリソースがある場合、文字列内のパターンのすべての出現をリンクに変換する必要があります。

現在、プロセスは次のとおりです: - re.sub を使用して非常に長いテキスト文字列を処理します - re.sub がパターン一致を見つけると、そのパターンがデータベース内のエントリと一致するかどうかを調べる関数を実行します - 存在する場合は一致です。リンクをラップします。リンクを一致の周りにラップします。

問題は、データベースに何百ものヒットがある場合があることです。私ができるようにしたいのは、データベースへの単一の一括クエリです。

では、Python で正規表現を使用して一括検索と置換を行うことはできますか?

参考までに、コードは次のとおりです(好奇心のために、私が調べているパターンは法的引用用です):

def add_linked_citations(text):
    linked_text = re.sub(r'(?P<volume>[0-9]+[a-zA-Z]{0,3})\s+(?P<reporter>[A-Z][a-zA-Z0-9\.\s]{1,49}?)\s+(?P<page>[0-9]+[a-zA-Z]{0,3}))', create_citation_link, text)
    return linked_text

def create_citation_link(match_object):
    volume = None
    reporter = None
    page = None
    if match_object.group("volume") not in [None, '']:
        volume = match_object.group("volume")
    if match_object.group("reporter") not in [None, '']:
        reporter = match_object.group("reporter")
    if match_object.group("page") not in [None, '']:
        page = match_object.group("page")

    if volume and reporter and page: # These should all be here...
        # !!! Here's where I keep hitting the database
        citations = Citation.objects.filter(volume=volume, reporter=reporter, page=page)
        if citations.exists():
            citation = citations[0] 
            document = citation.document
            url = document.url()
            return '<a href="%s">%s %s %s</a>' % (url, volume, reporter, page)
        else:
            return '%s %s %s' % (volume, reporter, page)
4

2 に答える 2

1

これが明らかで間違っている場合は申し訳ありませんが (4 時間以内に誰もそれを提案していないことが心配です!)、すべての一致を検索し、すべてに対してバッチ クエリを実行してから (すべての一致があれば簡単です)、sub を呼び出してみませんか?結果のディクショナリを使用します(関数はディクショナリからデータを取得します)?

正規表現を 2 回実行する必要がありますが、とにかくデータベースへのアクセスが高価な部分のようです。

于 2013-06-21T01:26:41.093 に答える
1

finditerwhich return match オブジェクトを使用することにより、単一の正規表現パスでそれを行うことができます。

一致オブジェクトには次のものがあります。

  • 名前付きグループの辞書を返すメソッド、groupdict()
  • 元のテキストでの試合の開始位置と終了位置、span()
  • 元の一致するテキスト、group()

したがって、次のことをお勧めします。

  • を使用して、テキスト内のすべての一致のリストを作成しますfinditer
  • 一致したすべての固有のボリューム、レポーター、ページのトリプレットのリストを作成します
  • それらのトリプレットを検索します
  • 見つかった場合は、各一致オブジェクトをトリプレット ルックアップの結果と関連付けます
  • 元のテキストを処理し、一致スパンで分割し、ルックアップ結果を補間します。

のリストを組み合わせてデータベース検索を実装しましたQ(volume=foo1,reporter=bar2,page=baz3)|Q(volume=foo1,reporter=bar2,page=baz3)...。より効率的なアプローチがあるかもしれません。

テストされていない実装は次のとおりです。

from django.db.models import Q
from collections import namedtuple

Triplet = namedtuple('Triplet',['volume','reporter','page'])

def lookup_references(matches):
  match_to_triplet = {}
  triplet_to_url = {}
  for m in matches:
    group_dict = m.groupdict()
    if any(not(x) for x in group_dict.values()): # Filter out matches we don't want to lookup
      continue
    match_to_triplet[m] = Triplet(**group_dict)
  # Build query
  unique_triplets = set(match_to_triplet.values())
  # List of Q objects
  q_list = [Q(**trip._asdict()) for trip in unique_triplets]
  # Consolidated Q
  single_q = reduce(Q.__or__,q_list)
  for row in Citations.objects.filter(single_q).values('volume','reporter','page','url'):
    url = row.pop('url')
    triplet_to_url[Triplet(**row)] = url
  # Now pair original match objects with URL where found
  lookups = {}
  for match, triplet in match_to_triplet.items():
    if triplet in triplet_to_url:
      lookups[match] = triplet_to_url[triplet]
  return lookups

def interpolate_citation_matches(text,matches,lookups):
  result = []
  prev = m_start = 0
  last = m_end = len(text)
  for m in matches:
    m_start, m_end = m.span()
    if prev != m_start:
      result.append(text[prev:m_start])
    # Now check match
    if m in lookups:
      result.append('<a href="%s">%s</a>' % (lookups[m],m.group()))
    else:
      result.append(m.group())
  if m_end != last:
    result.append(text[m_end:last])
  return ''.join(result)

def process_citations(text):
  citation_regex = r'(?P<volume>[0-9]+[a-zA-Z]{0,3})\s+(?P<reporter>[A-Z][a-zA-Z0-9\.\s]{1,49}?)\s+(?P<page>[0-9]+[a-zA-Z]{0,3}))'
  matches = list(re.finditer(citation_regex,text))
  lookups = lookup_references(matches)
  new_text = interpolate_citation_matches(text,matches,lookups)
  return new_text
于 2013-06-21T11:02:05.740 に答える