Как я могу использовать JSONiq для запроса объектов JSON в программе Python, работающей на моем ПК?
Мне удалось использовать JSONiq в блокноте Jupyter, работающем внутри кода Visual Studio, но меня интересует использование JSONiq в обычной программе Python (*.py), а не в блокноте (*.ipynb).
Поскольку он использует магию строк и ячеек для работы, когда вы находитесь в Jupyter Notebook с ядром Python, вы бы использовали его так же, как и любую другую магию, я подозреваю? См. «Как запустить магию IPython из скрипта» . Обязательно попробуйте текущий синтаксис, см. этот комментарий.
@Уэйн: Спасибо. Я попробовал решение в сообщении, на которое вы дали ссылку, но вызов функции get_ipython()
возвращает None
, и это вызывает ошибку.
@Barmar Потому что, когда я скопировал и вставил из блокнота Jupyter, я получил следующее сообщение об ошибке: %load_ext rumbledb SyntaxError: invalid syntax
.
Я вижу, вы отметили проблему как решенную, используя решение Гислена. Кажется, это по сути то, что я пытался сказать, но имеет более современный код с правильным синтаксисом. Или, может быть, более гибкий код для выполнения того, что я пытался поощрять. Плюс, определенно тщательный, потому что в нем также есть специфичные для пакета вещи, которые вам понадобятся, чтобы преодолеть первоначальный подход, который я поощрял. .... Мне нужно будет больше разобраться, почему get_ipython()
могут возникнуть проблемы.
@Wayne Мне удалось заставить его работать в блокноте Jupyter.
А как насчет использования from IPython.core.magic import Magics, cell_magic, magics_class
from здесь? Это работало напрямую в Python?
@Wayne Да, если вы вообще избегаете магии, ее можно запустить в программе Python, не предназначенной для ноутбука.
Хорошо. Однако я больше имел в виду, что шаг импорта у вас работает? Итак, вы можете получить доступ к IPython из вашего Python. Итак, я не понимаю, почему from IPython import get_ipython
в строке, за которой следует ip = get_ipython()
, в Python не работает?
@Уэйн, я тоже этого не понимаю.
Хорошо. Он работает везде, где я только что пробовал использовать Python (всего три: один локальный и два удаленных с разными версиями), и поэтому это определенно странно. К счастью, более полнофункциональный вариант Ghislain поможет вам преодолеть это.
Между Python (вне записной книжки) и работающим экземпляром RumbleDB (движок JSONiq) можно взаимодействовать через HTTP. Это можно сделать, скопировав тот же код, который работает в блокнотах Jupyter для поддержки магии %jsoniq, в свой собственный скрипт Python. Все это с открытым исходным кодом и доступно здесь:
https://github.com/RumbleDB/pyrumble/blob/main/src/rumbledb/rumbledb.py
Вы, конечно, можете адаптировать сигнатуру функции по своему вкусу и потребностям.
Запуск сервера (так же, как для ноутбуков)
Как и в случае с ноутбуками Jupyter, сервер RumbleDB может работать где угодно (локально на вашем ноутбуке или у облачного провайдера, такого как Amazon EMR и т. д.), если настройки брандмауэра делают порт доступным. Команда для запуска сервера, скажем, на порту 8001:
spark-submit path-to-rumbledb-1.21.jar serve -p 8001
Рекомендуется убедиться, что доступ к порту есть только у вас (например, запустив локально и с помощью брандмауэра или установив ограничения IP-адресов для облачного провайдера), поскольку это относительно простая реализация, не учитывающая безопасность.
Передача небольших объемов данных
Небольшие объемы данных могут быть переданы напрямую из Python в JSONiq через запрос, например. путем внедрения некоторого JSON в объявление глобальной или let переменной в строке запроса JSONiq, передаваемой в RumbleDB.
Небольшие объемы данных могут быть переданы из JSONiq в Python в результатах запроса, которые отправляются через HTTP обратно в Python в теле ответа.
Передача больших объемов данных
Большие объемы данных (csv, строки JSON, текст и т. д.) можно передать из Python в JSONiq, сохранив их в любой файловой системе, к которой у обоих есть доступ (локальная, S3, HDFS и т. д.), а затем прочитав их с помощью JSONiq с помощью соответствующий URI.
Большие объемы данных можно передать обратно из JSONiq в Python, если облачный провайдер поддерживает стандарт Apache Livy. В этом случае вместо этого вы можете скопировать и вставить этот код. При этом jar запускается как одно задание для каждого запроса, после чего можно указать выходной путь, в который RumbleDB должен записывать выходные данные. Это могут быть терабайты или даже питабайты данных, если они работают в кластере и записываются обратно в облачное хранилище.
У нас есть планы на будущее попытаться более тесно интегрировать JSONiq в Python (что позволило бы «встроить» RumbleDB в Python для более естественного взаимодействия через память, а не через файловую систему), но на данный момент у нас нет четких дат.
Спасибо. Была бы очень признательна за более тесную интеграцию JSONiq в Python. JSONiq, кстати, фантастический. Он делает это правильно (в отличие от других языков запросов JSON).
Я рад это слышать! Это еще больше мотивирует нас повышать приоритет. Также спасибо за приятные слова в адрес JSONiq!
import json
import requests
# Replace the port number
# by the port you used
# to run rumbledb from the command line,
# in my case:
# spark-submit rumbledb-1.21.0-for-spark-3.5.jar serve -p 9090
__RUMBLEDB_PORT__ = 9090
# Code adapted from the rumbldb source code
# for the jsoniq cell magic at
# https://github.com/RumbleDB/pyrumble/blob/main/src/rumbledb/rumbledb.py
# as suggested by Ghislain in his answer above
# https://stackoverflow.com/a/78768053/1818935
# The _python_ value corresponding to the JSON value
# that is meant to be the query's target
# should be passed as the parameter target.
# Use three consecutive dollar symbols: $$$
# to represent the target JSON value in the query.
def jsoniq(target:str, query:str) -> list:
resolved_query = query.replace('$$$', json.dumps(target))
rumbledb_url = f'http://localhost:{__RUMBLEDB_PORT__}/jsoniq'
response = json.loads(requests.post(url=rumbledb_url, data=resolved_query).text)
if 'warning' in response:
print(json.dumps(response['warning']))
if 'values' in response:
return response['values']
elif 'error-message' in response:
print(response['error-message'])
else:
print(response)
Тестовые запросы:
for item in jsoniq({'x': 3}, '$$$.x'):
print(json.dumps(item))
Вывод: 3
for item in jsoniq([{'x': 11}, {'x': -3}, {'x': 42}], '$$$[][$$.x gt 0]'):
print(json.dumps(item))
Выход:
{"x": 11}
{"x": 42}
Это фантастика. Мы рассмотрим (отдавая вам должное) расширение пакета pip дополнительной функцией в этом направлении. Кстати, то, что вы делаете с символом $$$, на самом деле можно сделать с помощью предоставленного извне элемента контекста в стандарте W3C с помощью «объявить элемент контекста как внешний массив; $$[][$$.x gt 0 ]" Однако нам необходимо убедиться, что API RumbleDB поддерживает получение сериализованного объекта или массива через параметр элемента контекста CLI или HTTP. В настоящее время он анализирует только атомарные значения как элемент внешнего контекста, но это временное ограничение.
Еще одно предложение по улучшению: насколько я понимаю, вы создаете запрос с помощью parse-json(), передавая сериализованный объект (или массив), например, parse-json("{\"x\": 3}").x. Но на самом деле любой строго правильно сформированный контент JSON также может быть напрямую скопирован «как есть» в JSONiq, т. е. доставка через {"x":3}.x в Rumble DB дает тот же результат, что и доставка через синтаксический анализ. json("{\"x\": 3}").x, занимая при этом меньшую полосу пропускания. Другими словами, вы можете заменить $$$ напрямую результатами одного уровня json.dumps() без необходимости использования дополнительного json.dump()+parse-json().
@GhislainFourny Спасибо. Я упростил функцию согласно вашему второму замечанию. Однако мне не удалось заставить declare context item as array external;
работать.
Это выглядит здорово! Действительно, что касается объявления пролога контекстного элемента, в моем предыдущем комментарии я имел в виду, что оно требует исправления непосредственно в jar-файле RumbleDB. Я внесу его в список для будущего выпуска.
Еще я заметил, что вы используете сервер public.rumbledb.org (но возможно это только для публичного кода). Это, конечно, нормально (это пул микроэкземпляров, автоматически возобновляемых каждые 24 часа, предназначенный для удобства и игры). Однако это означает, что приведенный выше код отправляет запрос на этот сервер, а не на сервер, который вы запускаете с помощью команды spark-submit. Если вы используете собственный сервер на своем компьютере, вы можете использовать localhost в URI.
@GhislainFourny Боже мой, спасибо! Вот объяснение этой проблемы! Это сводило меня с ума.
Очень рад помочь! Действительно, это, вероятно, объяснение. По крайней мере, это показывает, что наша облачная установка устойчива и продолжает работать, несмотря ни на что 😊
Как вы думаете, почему использование будет другим?