2017-11-04

JSON-поля MySQL в Битриксе

Начиная с версии 5.7 в MySQL появились JSON-поля. Их можно использовать для хранения данных произвольной структуры, ведь не всегда удобно создавать дополнительные таблицы и поля. Немного NoSQL в MySQL никому не навредит, а наоборот упростит разработку.
Рассмотрим возможность использования JSON-полей в Битриксе.

Для начала желательно ознакомиться с документацией по JSON Data Type.
Далее, в Битриксе нужно завести свою сущность в базе (можно highloadblock). В сущности должно присутствовать поле типа JSON (в случае с highloadblock - сначала создается текстовое поле, а затем SQL-запросом меняется его тип на JSON). В ORM-описании сущности для поля указывается тип "text" (см. ниже).

Примерные данные в сущности:

ORM сущности:
namespace Bitrix\Nosql;
use Bitrix\Main, Bitrix\Main\Localization\Loc;
class NosqlTable extends Main\Entity\DataManager
{
 public static function getTableName()
 {
  return 'hb_nosql';
 }
 public static function getMap()
 {
  return array(
   'ID' => array(
    'data_type' => 'integer',
    'primary' => true,
    'autocomplete' => true,
    'title' => 'id',
   ),
   'UF_JSON' => array(
    'data_type' => 'text',
    'title' => 'uf_json',
   ),
  );
 }
}
Примеры работы с JSON-полем:
use \Bitrix\Nosql\NosqlTable;
use \Bitrix\Main\Entity\ExpressionField;
use \Bitrix\Main\DB\SqlExpression;
require_once('local/nosql.php');

// добавление записи с JSON-полем делается как с обычным текстовым полем
NosqlTable::add([
    'UF_JSON' => json_encode(['key3' => 'value three', 'key5' => 'value five', 'key7' => 'value seven']),
]);

// обновление поля
NosqlTable::update(2, [
    'UF_JSON' => new SqlExpression('JSON_REPLACE(UF_JSON, "$.key2", ?)', rand())
]);

// пример фильтра по ключу JSON-объекта
\Bitrix\Main\Application::getConnection()->startTracker();
$res = NosqlTable::getList([
    'select' => ['ID', 'UF_JSON', 'json_key1'],
    'filter' => [
 '=json_key1' => 'val1',
    ],
    'runtime' => [
 new ExpressionField('json_key1', 'UF_JSON->>"$.key1"'),
    ]
]);
echo $res->getTrackerQuery()->getSql()."\n";
$rows = $res->fetchAll();
\Bitrix\Main\Application::getConnection()->stopTracker();
print_r($rows);
Результат:
SELECT 
 `nosql_nosql`.`ID` AS `ID`,
 `nosql_nosql`.`UF_JSON` AS `UF_JSON`,
 UF_JSON->>"$.key1" AS `json_key1`
FROM `hb_nosql` `nosql_nosql` 

WHERE UF_JSON->>"$.key1" = 'val1'
Array
(
    [0] => Array
        (
            [ID] => 1
            [UF_JSON] => {"key1": "val1", "key2": "val2"}
            [json_key1] => val1
        )

)

Небольшой бонус: чтобы работать со значениями JSON-полей без их преобразований в строку при добавлении/обновлении и перевода из строки при чтении, то можно воспользоваться возможностями ORM Битрикса "save_data_modification" и "fetch_data_modification".