Полнотекстовый поиск XML-данных с помощью Python: лучшие практики, плюсы и минусы

Task

Я хочу использовать Python для полнотекстового поиска XML-данных.

Примеры данных

<elements>
  <elem id="1">some element</elem>
  <elem id="2">some other element</elem>
  <elem id="3">some element
    <nested id="1">
    other nested element
    </nested>
  </elem>
</elements>

Основные функции

Самая основная функциональность, которую я хочу, состоит в том, что поиск "другого" в XPath ( "/elements/elem" ) возвращает хотя бы значение атрибута ID для соответствующего элемента (elem 2) и вложенного элемента (elem 3, вложенные 1) или соответствующие XPaths.

Идеальная функциональность

Решение должно быть гибким и масштабируемым. Я ищу возможные комбинации этих функций:

  • поиск вложенных элементов (бесконечная глубина)
  • атрибуты поиска
  • поиск предложений и абзацев
  • поиск с использованием подстановочных знаков
  • поиск с использованием нечеткого соответствия
  • вернуть точную информацию соответствия
  • хорошая скорость поиска для больших файлов XML

Вопрос

Я не ожидаю решения со всей идеальной функциональностью, мне придется комбинировать различные существующие функции и сам код. Но сначала я хотел бы узнать больше о том, что там есть, какие библиотеки и подходы вы обычно использовали для этого, каковы их плюсы и минусы.

EDIT: Спасибо за ответы до сих пор, я добавил детали и начал щедрость.

Ответ 1

Не уверен, что этого будет достаточно для ваших нужд, но lxml поддерживает регулярные выражения в xpath (что означает: вы можете использовать xpath 1.0 плюс функции расширения EXSLT для регулярных выражений)

По сравнению с списком функций, который был добавлен позже:

  • поиск вложенных элементов (бесконечная глубина): yes
  • атрибуты поиска: yes
  • поиск предложений и абзацев: нет. Предполагая, что "абзацы" являются фактическими элементами xml, тогда да. Но "предложения" как таковые, нет.
  • поиск с использованием подстановочных знаков: да (регулярные выражения)
  • поиск с использованием нечеткого соответствия: нет (предполагается, что это происходит, синонимы и т.д.)
  • вернуть точную информацию о согласии: да
  • Хорошая скорость поиска для больших файлов XML: да, за исключением случаев, когда ваши файлы настолько велики, что вам действительно нужен полнотекстовый индекс, чтобы получить хорошую скорость в любом случае.

Единственный способ удовлетворить весь ваш запрос, который я вижу, - это загрузить ваши файлы в собственную XML-базу данных, которая поддерживает "реальный" полнотекстовый поиск (возможно, через полнотекстовый интерфейс XQuery) и использовать это. (не может помочь вам в дальнейшем, возможно, Sedna, который, похоже, имеет API-интерфейс python и, кажется, поддерживает полнотекстовый поиск?)

Ответ 2

Я думаю, вам лучше всего использовать полнотекстовый поисковый движок, такой как Solr: http://lucene.apache.org/solr/

Что вы можете сделать, это сохранить "документ" в solr для каждого <elem /> в вашем xml. Вы можете хранить любые данные, которые вам нравятся в документе. Затем вы можете выполнить поиск по индексу и захватить поле id, хранящееся в соответствующих документах. Это будет очень быстро для большого набора документов.

Ответ 5

Итак, недавно мне пришлось создать конвертер XML в JSON. Он точно не соответствует стандарту JSON, но подходит довольно близко. Функция xml2json возвращает представление словаря объекта xml. Все атрибуты элементов включены в словарь с ключом атрибутов, а текст текста включен в текстовый ключ.

Например, ваш объект xml будет выглядеть так после его преобразования:

json = {'elements': 
    {'elem': [
        {'attributes': {'id', '1'}, 'text': 'some element'},
        {'attributes': {'id', '2'}, 'text': 'some other element'},
        {'attributes': {'id', '3'}, 'text': 'some element', 'nested': {
            'attributes': {'id', '1'}, 'text': 'other nested element'}},
    ]}

Вот функция xml2json.

def xml2json(x):
    def get_attributes(atts):
        atts = dict(atts)
        d = {}
        for k, v in atts.items():
            d[k] = v.value
        return d

    def get_children(n, d):
        tmp = {}
        d.setdefault(n.nodeName, {})
        if n.attributes:
            tmp['attributes'] = get_attributes(n.attributes)
        if n.hasChildNodes():
            for c in n.childNodes:
                if c.nodeType == c.TEXT_NODE or c.nodeName == '#cdata-section':
                    tmp['text'] = c.data
                else:
                    children = get_children(c, {})
                    for ck, cv in children.items():
                        if ck in d[n.nodeName]:
                            if not isinstance(d[n.nodeName][ck], list):
                                tmpv = d[n.nodeName][ck]
                                d[n.nodeName][ck] = []
                                d[n.nodeName][ck].append(tmpv)
                            d[n.nodeName][ck].append(cv)
                        else:
                            d[n.nodeName][ck] = cv

        for tk, tv in tmp.items():
            d[n.nodeName][tk] = tv

        return d

    return get_children(x.firstChild, {})

Вот функция searchjson.

def searchjson(sobj, reg):
    import re
    results = []
    if isinstance(sobj, basestring):
        # search the string and return the output
        if re.search(reg, sobj):
            results.append(sobj)
    else:
        # dictionary
        for k, v in sobj.items():
            newv = v
            if not isinstance(newv, list):
                newv = [newv]

            for elem in newv:
                has_attributes = False
                if isinstance(elem, dict):
                    has_attributes = bool(elem.get('attributes', False))
                res = searchjson(elem, reg)
                res = [] if not res else res
                for r in res:
                    r_is_dict = isinstance(r, dict)
                    r_no_attributes = r_is_dict and 'attributes' not in r.keys()
                    if has_attributes and r_no_attributes :
                        r.update({'attributes': elem.get('attributes', {})})

                    results.append({k: r})

    return results

Функция поиска, которую я создал после прочтения вашего вопроса. Он не был проверен на 100% и, вероятно, имеет несколько ошибок, но я думаю, что это будет хорошим началом для вас. Что касается того, что вы ищете, он ищет вложенные элементы, атрибуты, используя подстановочные знаки. Он также возвращает идентификатор элементов.

Вы можете использовать такую ​​функцию, где xml - объект xml для поиска, а reg - строка шаблона регулярного выражения для поиска, например: "other", "oth. *", ".the". все будут находить элементы с "другими" в них.

json = xml2json(xml)
results = searchjson(json, reg='other')

будут представлены словари.

Надеюсь, что это поможет.

Ответ 6

Для одиночных больших файлов, к которым обращается Python script, вы должны посмотреть Xapian, это полнофункциональная полнотекстовая индексация и поисковая система, зрелая и надежная, и имеет замечательные привязки Python первого класса. Он работает с Python, как это было написано на Python, без внешних серверов для запуска или с такой глупостью.

Если вам не нужно сохранять индексы и использовать базу данных в памяти, это будет очень быстро. Это быстрее, чем решения на основе Lucene, и использует небольшую часть ресурсов.