Обновить
4
0
Alexander Irbis@BlessMaster

Разработчик

Отправить сообщение
Как тут уже написал zzashpaupat, внешний источник данных можно явно объявить через CREATE FOREIGN TABLE и делать запросы к двум базам таким образом. Как приятный довесок другой базой могут быть не только базы Postgres, но, и тот же MySQL, Redis, Mongo, MemCache, простые файлы на диске — всё, на что будет написан соответствующий враппер. Гуглить FDW.

Вот: http://pgxn.org/tag/fdw/, если покопаться, даже к твиттеру есть.

Но вообще, запросы к двум базам — это достаточно экстремальное занятие класса «100 избранных способов прострелить себе ногу», особенно, если мы используем СУБД чуть больше, чем хранилище для статей в любимом бложике и мы рассчитываем на согласованное состояние данных под нагрузками и в распределённых системах, это хозяйство требует хорошо прокачанных скиллов уровнем не ниже совета джедаев :-) Не уверен, что «начинающему пользователю» это «ну очень надо».
Когда мне нужен был FTS для предыдущего проекта, я использовал словарь ispell, установленный в системе, вручную сконвертировав его в utf-8. Сейчас, да, ispell объявлен устаревшим и давно уже отсутствует в моей системе (если нужно — могу поделиться).
В этом году я вернулся к этой теме, но обстоятельства уже немного поменялись: python + pymorphy2 (со словарями opencorpora.ru) + jsonb — это небо и земля, по сравнению с тем, что было, когда FTS появился в Postgres. (Кстати, jsonb, как и hstore ранее, и индексы GiST и GIN, применяемые для всего этого, создавались одними и теми же людьми).
FTS — это вообще очень непростое и капризное блюдо, которое нужно готовить как рыбу Фугу и качественные словари — основа всего. К сожалению действительно качественных и при этом бесплатных в свободном доступе для русского языка — нет. И вряд ли в ближайшее время появится. Словари от opencorpora в сочетании с pymorphy, прямо скажем, на голову лучше всего остального, с чем я сталкивался, но… совершенству предела нет, а русский язык — не самый простой, плюс ещё много факторов, добавляющих к ложке дёгтя.
То есть, подытоживая, postgres — это не более чем коробка с инструментами. Инструменты качественные, с большими возможностями, но сами по себе они «ничто». FTS в постгрес — это фреймворк для создания собственных решений (который можно использовать как целиком, так и по частям), плюс пара утилит, чтобы набросать что-то на скорую руку и продемонстрировать концепцию (то же самое можно сказать про SQL в принципе — инструмент обработки данных не может заменить сами данные).
Sphinx — это уже готовый специализированный продукт, имеющий какое-то завершённое и оттестированное решение. Наверняка «изкоробки» он будет работать лучше, чем фреймворк, в котором ещё нужно разобраться. Но я не уверен, что он будет работать лучше чем Postgres при прочих равных (использование открытых свободно-распространяемых словарей). Я успешно искал по сводной базе объявлений, думаю, книги — не более сложная задача. Но, если нужно «работает уже вчера» и всё устраивает, то может действительно стоит предпочесть готовое решение.
В консоли постгрес работает автодополнение, которое может показать все базы, таблицы, поля и т.д. прямо в процессе написания запроса (или если включён bashcomp, то и при запуске psql — подсказывает имеющиеся локально базы).

Более того, в postgres в каждой базе есть две стандартных схемы: «pg_catalog» и «information_schema», из которых можно извлечь абсолютно всю информацию о базе. Набросайте себе несколько хранимок и пользуйтесь ими как расширением. В интернете много готовых рецептов на разные случаи жизни и возможности выходят далеко за рамки SHOW TABLES, DESCRIBE DATABASE.

С порядком столбцов, к сожалению, решения вроде как нет. Но по большей части он и не имеет смысла обычно, так что, вряд ли это реализуют в обозримой перспективе. Внутренний перфекционист, конечно, возмущён, но приходится смириться. В крайнем случае функция или вьюха решают проблему, если нужно много работать с таблицами из консоли.
Ну так это и разные слова, если уже на то пошло (и русский язык в этом не виноват). Точно так же регулярки бессильны сопоставить «человек» и «люди», что элементарно для FTS с полноценной морфологией. Вхождение же «хабр» в «хабрахабр» — лишь частный случай, далеко не универсальный.
Для разных же слов существуют тезаурусы, LSI, WordNet и прочие высшие материи.
Чистый же FTS — не про это, хотя для некоторых задач достаточно даже простого стеммера вместо словарей — дёшево и сердито.
Но в любом случае FTS — это не серебряная пуля, и нужно понимать, какая цель и какой инструмент лучше подходит.
Вообще очень рекомендую вот эту замечательную книгу: http://postgresql.leopard.in.ua/
Помогает понять и решить очень многое.
P.S. справедливости ради: функции из sqlutils также можно встроить и в окружение Jinja и сильно упростить пример из комментариев выше.

{% sql 'get_countries_by_conds', note='get countries by date conditions or ids' %}
    SELECT *
    FROM "countries"
    {{ where(
            ids != None and "id {}".format(in_list(ids)),
            date_from and "creation_date >= {}".format(wrap_dt(date_from)),
            date_to and "creation_date <= {}".format(wrap_dt(date_to))
        ) }}
    ORDER BY "creation_date" ASC
{% endsql %}



Но, к сожалению, останется сбивающая с толку специфика вроде 'ids != None' вместо 'ids is not None', отсутствие списочных выражений и тому подобные «мелочи», вместо которых придётся писать по старинке map/filter.
Имхо, Jinja — лишнее звено в данной схеме.

Jinja — очень узкое подмножество Python, дополненное средствами безопасности (песочница для кода от третьих лиц), кешированием фрагментов, средствами перевода и прочей обработки текста.

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

То же решение «в лоб» в чистом Python (ох плачет по нему напильник: делать работу драйвера по вставке данных и нарываться на SQL-injection — неблагодарное занятие)

# --- sqlutils.py -------------------------------


def wrap_int(num):
    assert isinstance(num, int)
    return str(int(num))


def wrap_dt(dt):
    assert isinstance(dt, datetime.datetime)
    return "'{}'".format(dt.isoformat())


def in_list(lst, wrap=wrap_int):
    return "IN ({})".format(", ".join(wrap(x) for x in lst))


def select(*args):
    if args:
        return "SELECT {}".format(", ".join(args))
    else:
        return "SELECT *"


def where(*args, glue="AND"):
    args = [x for x in args if x]
    if args:
        return "WHERE {}".format(" {} ".format(glue).join(args))
    else:
        return ""


def order_by(column, asc=True):
    return "ORDER BY {} {}".format(column, asc and "ASC" or "DESC")


# --- model.py ----------------------------------


def sql_countries_by_conds(ids=None, date_from=None, date_to=None, asc=True):
    """ get countries by date conditions or ids """

    return """

SELECT *
FROM "countries"
{where}
ORDER BY "creation_date" {asc}

""".format(
        where=where(
            ids is not None and "id {}".format(in_list(ids)),
            date_from and "creation_date >= {}".format(wrap_dt(date_from)),
            date_to and "creation_date <= {}".format(wrap_dt(date_to))
        ),
        asc=asc and "ASC" or "DESC"
    )



Какие преимущества перед Jinja?

1. Всё компактно — не нужно продираться через дебри разметки, не нужно бегать по коду и файлам, не нужно держать в памяти (и ошибаться) из чего же устроен наш запрос.
2. Работает навигация IDE и анализатор кода с рефакторингами и автодополнениями — в любую функцию можно прыгнуть, ошибки и подозрительные места подсвечиваются.
3. Это поддаётся пошаговой отладке, сюда можно поставить точку остановки и проинспектировать состояние.

Возможно я отстал от жизни и последние два пункта с Jinja тоже не проблема в передовых IDE, но первый пункт — никуда не денешь. Ну и всю мощь «питона с батарейками» в противовес «песочнице» я уже упоминал (можно, конечно, перенастроить песочницу в швейцарский нож, благо она это позволяет, но создавалась она явно с противоположной целью и умолчания — против нас).

Как-то так.
Нужно индексировать не просто слова, а их устойчивые сочетания, при этом с учётом синонимов, поскольку в разных комбинациях синонимы могут сильно отличаться (например, «крутая тачка» может быть заменено «дорогой автомобиль», а «двухколёсная тачка» — нет).
Ранжирование (и релевантность) существенно различается в зависимости от расстояние между словами, их нахождения в одном предложении, в соседних предложениях, и т.д.
Это если кратко.
Если не очень кратко, то вот здесь просто кладезь для жаждущих: research.yandex.ru/lib/researches/?theme=web-mining-and-search
Очень интересно почитать старые документы, например вот этот: download.yandex.ru/company/iworld-3.pdf
Если в результате багов будут теряться миллионы, или хуже того — умирать люди, о «непозволительной роскоши» придётся рассказывать не в комментариях
Слабые стороны данной реализации:
* не справляется с острыми углами, аппроксимируя их в гладкие петли.
* может развернуть петлю в противоположную сторону, что совсем не то, чего хотелось бы.
* простым «прямым» (гладким) участкам уделяется много внимания, получается очень детализированный механизм для простого, который в итоге строит не сглаженный длинный штрих, а очень ломанный (здесь уже выше приводили примеры), в то время как сложные участки с завитками — слишком обделены вниманием.

Но вообще, отличная вещь как старт для разработок, за что отдельное спасибо.
Единственное, не нашёл ни одного упоминания слова «лицензия».
Плюс Hiew — незаменимейшая вещь, спасавшая не раз.
Смотря что Вы имеете в виду «долго». Минимальный результат — миллисекунды или меньше, сколько там нужно, когда цифры ложатся ровно на свои места, максимальный — время полного перебора, когда перебор не завершается нахождением первого найденного ответа, когда удачной является последняя комбинация в переборе.
test_freuser_ft_alex_2 — это как раз был вариант доработанный до выхода сразу при нахождении первого ответа. Про жульничество я написал чуть выше — это «жульничество» сразу напрашивается на оптимизацию, как только обнаруживается, что разный порядок обработки даёт разную скорость и разница существенна нахождения единственного ответа. test_interval — это уже окончательно оптимизированный вариант.

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

К сожалению как оптимизировать Ваш вариант мне слёту в голову не пришло, а надо было просто немного уделить этому внимание. Вот новый вариант

gist.github.com/alexander-irbis/34828a5e9dc41940f506

test_freuser_ft_alex_3 — это дополненная «жульничеством» с m функция. В PyPy этот вариант лидирует с отрывом.

Ещё интересно взглянуть на результат test_dict_str2int — это первоначальный вариант, который я использовал, пока не обнаружил, что преобразование из строки в число — это дорого.
Изначально sorted ставились для однозначности порядка вычисления и сравнения разных интерпретаторов и механизмов работы с памятью в python. Поскольку порядок играет роль, то в случае использования несортированного множества (set), при каждом запуске порядок элементов в множестве различается, задача найти единственное решение даёт достаточно широкий разброс времени выполнения от долей секунды до десятков секунд и это лишает смысла сравнение.

Вытаскивание m на первое место — это как заметил freuser — действительно «жульничество», но это уже в самой статье обсуждённый вариант — m равна единице и ничему иному и это ускоряет перебор на порядок; постановка её на первое место — автоматически делает её единицей. Если продолжить идти путём жульничества, то мы изначально сразу знаем m, s и o и уже только это сокращает тупой перебор в 720 раз. А если идти до конца, то, как тут выше в комментариях справедливо заметили, всё значительно проще решается на бумажке.
Три шага, двадцать четыре варианта решения, бумажка в отличие от оригинальной задачи не требуется. Несерьёзно.
Более того, для ряда задач отпадает необходимость вообще в задействовании связей — Postgres предлагает ряд приятных «дополнительных» типов вроде Array, HStore, JSON(B).
Constraint — на единую таблицу entities.
Без дополнительного запроса к этой таблице тип автоматически известен не станет, здесь никакой магии.
Магия появляется с адресацией — нет необходимости указывать тип ресурса в дополнение к id.
И у данной схемы также есть свои минусы: таблица entities станет узким местом в системе, с одной стороны требуя для своего поддержания дополнительные индексы по большому количеству записей, с другой — обслуживание этой таблицы (в системах требующих такого обслуживания) межет стать проблемой. Так же как мы захотели сделать единую систему комментариев для статей, следом за статьями нам рано или поздно захочется сделать сущностями и сами комментарии, для какой-нибудь системы оценок и рассчёта рейтинга. Хотя этот вариант мне кажется лучшим для небольших систем, но к нему стоит также подходить осторожно.
Технология организации социума. Причём технология достаточно фундаментальная, чтобы заинтересовать каждого.
Смена пароля — это вообще хорошая практика, если есть подозрение на взлом. Но заранее Вы не можете быть уверены, взломали Вас или заразили (или и то, и другое — вирус вполне может умыкнуть и пароль в некоторых обстоятельствах).

Информация

В рейтинге
Не участвует
Откуда
Киев, Киевская обл., Украина
Дата рождения
Зарегистрирован
Активность