> For the complete documentation index, see [llms.txt](https://navixy.com/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://navixy.com/docs/user/ru/guide/account/iot-logic/nodes/missing-values-in-expressions.md).

# Отсутствующие значения в выражениях

Поймите, почему формула или условие IoT Logic не возвращает значение, когда отсутствует нужное ей значение, и какие шаблоны сравнения предотвращают это.

Формулы и условия в IoT Logic работают со значениями, которые отправляет GPS-устройство. Когда значения, которое нужно формуле, нет, большинство операторов не могут выдать результат, и формула вообще не возвращает ничего: ни `false`, и не ноль. На этой странице объясняется, когда значение пропадает, как выглядит сбой и как писать формулы и условия, которые продолжают работать. Она предназначена для всех, кто настраивает поток IoT Logic, и не предполагает знаний программирования.

Страница относится к обоим местам, где вы пишете выражение: к **Формула** полю [Вычисление атрибута](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node.md) узла **Выражение условия (JEXL)** полю [IF/THEN Логика](/docs/user/ru/guide/account/iot-logic/nodes/logic-node.md) Узел.

## Когда выражение не может выдать значение

Пустое значение останавливает всё выражение. Это относится к любой стороне вычисления, сравнению размера, проверке текстового префикса или логической операции `!`, `&&`, или `||`. В формуле и в Анализ данных пустое значение отображается как `null`.

Чтение атрибута, который GPS-устройство никогда не отправляло, само по себе не является ошибкой. Останавливается оператор, которому затем нужно работать с пустым значением. Одно пустое значение останавливает всё выражение целиком, включая те части, где значения были. Условие, которое проверяет четыре геозоны, останавливается на первой, которая не может ответить, а остальные три даже не проверяются. Исключение — терм, который стоит раньше в выражении и уже определил результат.

## Что вы видите, когда выражение не выдаёт ничего

Ничего не сообщает о сбое. Результат зависит от того, в каком узле находится выражение:

| Где выполняется выражение                                 | Что происходит с этим пакетом данных                                                                   |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| **Формула** полю **Вычисление атрибута** узел             | Атрибут не имеет значения для этого пакета, и Анализ данных показывает его как `null`.                 |
| **Выражение условия (JEXL)** полю **IF/THEN Логика** узел | Данные уходят по соединению ELSE, а узел сохраняет свой атрибут как пустое значение, а не как `false`. |

{% hint style="warning" %}
Ничего не сообщает о сбое. В потоке не появляется ошибка, счётчик не меняется, а все остальные атрибуты по-прежнему вычисляются нормально. Единственный признак — само отсутствующее значение, поэтому поток может выглядеть исправным, хотя один атрибут не выдаёт значение неделями.

Сохранение потока тоже не доказывает, что формула работает. Проверка, которая запускается при сохранении, подтверждает лишь, что выражение записано правильно. Она не выполняет выражение на данных вашего GPS-устройства, поэтому формула, которая не выдаёт ничего в каждом пакете, всё равно сохраняется без предупреждения.
{% endhint %}

## Как каждый оператор обрабатывает пустое значение

Четыре оператора всегда продолжают работать с пустым значением: `==`, `!=`, `=~`, и `!~`. Операторы `&&` и `||` продолжают работать только тогда, когда сторона, которую читают первой, уже определила ответ. Все остальные операторы останавливаются.

Эта таблица охватывает атрибут, на который вы ссылаетесь по имени, то есть форму, которую [автозаполнение](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node/managing-attributes.md#autofill-attribute-names) подставляет. Четыре оператора ведут себя по-разному, когда значение приходит из `value()` вместо этого. См. [Чтение по имени или с помощью value()](#reading-by-name-or-with-value).

| Оператор                                                   | При пустом значении с любой стороны                                                                                                        |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `==`, `!=` (равно, не равно)                               | Возвращает настоящий ответ «да» или «нет». `null == null` — это `true`, и `null != null` — это `false`.                                    |
| `=~`, `!~` (в списке или соответствует текстовому шаблону) | Возвращает настоящий ответ «да» или «нет». Пустое значение никогда не совпадает, поэтому `=~` возвращает `false` и `!~` возвращает `true`. |
| `??` (использовать запасной вариант)                       | Возвращает значение справа. Оно подставляется только вместо пустого значения.                                                              |
| `?:` (использовать запасной вариант)                       | Возвращает значение справа. Оно также подставляется вместо `false`, для `0`, и для пустого текста.                                         |
| `a ? b : c` (выбор между двумя значениями)                 | Выбирает `ветку c` , так же как и для `false`.                                                                                             |
| `=^`, `!^`, `=$`, `!$` (начинается с, заканчивается на)    | Выражение не выдаёт ничего.                                                                                                                |
| `<`, `<=`, `>`, `>=` (сравнение размеров)                  | Выражение не выдаёт ничего.                                                                                                                |
| `+`, `-`, `*`, `/`, `%` (арифметика)                       | Выражение не выдаёт ничего.                                                                                                                |
| `&`, `\|`, `^` (битовые операции)                          | Выражение не выдаёт ничего.                                                                                                                |
| `!` (не)                                                   | Выражение не выдаёт ничего.                                                                                                                |
| `&&`, `\|\|` (и, или)                                      | Выражение не выдаёт ничего, если только сторона, которую читают первой, не определит ответ.                                                |

Шесть текстовых операторов делятся на две группы. `=~` и `!~` переносят пустое значение, но `=^`, `!^`, `=$`, и `!$` не делают этого, когда атрибут указан по имени. Условие вроде `hardware_key !^ 'AB'` выглядит как безопасный способ исключить префикс, но оно ничего не возвращает для каждого пакета без `hardware_key`.

Вкладка `Математика:` функции ничего не возвращают, когда получают пустое значение, поэтому `math:round(engine_temp)` ничего не возвращает, когда `engine_temp` отсутствует. Эти `утилиты:` функции более снисходительны и вместо этого возвращают пустое значение, поэтому `util:join` и `util:joinNonNull` всё равно возвращают результат, когда один из их аргументов отсутствует. При объединении текста с `+` не терпит пустых значений: `'Зона: ' + zone_label` ничего не возвращает.

## Логические операторы с пустым значением

Порядок важен для обоих `&&` и `||`. Каждый оператор сначала читает левую часть и останавливается на ней, когда левая часть уже определяет результат. А `false` левая часть определяет результат `&&`, а `true` левая часть определяет результат `||`.

| Выражение                 | Результат                                                                                                     |
| ------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `false && (пусто)`        | `false`. Если левая часть ложна, оператор AND прерывает вычисление, поэтому правая часть никогда не читается. |
| `true && (пусто)`         | Ничего не возвращает.                                                                                         |
| `(пусто) && что угодно`   | Ничего не возвращает. Левая часть читается первой.                                                            |
| `true \|\| (пусто)`       | `true`. Если левая часть истинна, оператор OR прерывает вычисление, поэтому правая часть никогда не читается. |
| `false \|\| (пусто)`      | Ничего не возвращает.                                                                                         |
| `(пусто) \|\| что угодно` | Ничего не возвращает. Левая часть читается первой.                                                            |

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

## Чтение по имени или с помощью value()

Четыре оператора дают разные результаты для одного и того же отсутствующего значения в зависимости от того, как формула его интерпретирует. Эти операторы: `=^`, `!^`, `=$` и `!$`, которые проверяют начало или конец текстового значения.

| Формула                                | Результат при `идентификатор водителя` не было отправлено |
| -------------------------------------- | --------------------------------------------------------- |
| `идентификатор водителя !^ 'AB'`       | Ничего не производит                                      |
| `value('driver_id', 0, 'all') !^ 'AB'` | `true`                                                    |

Ссылка на атрибут по имени — это Форма, которая [автозаполнение](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node/managing-attributes.md#autofill-attribute-names) вставки, так что условие, построенное с помощью выбора атрибутов, выбирает первый путь. Запишите сравнение с `value()` когда вам нужно `!^` или `!$` сообщать `true` в пакете, который не содержит атрибут.

Все остальные операторы ведут себя одинаково в обеих формах. Сравнение по размеру, арифметика и `!` в любом случае ничего не дают, и `==`, `!=`, `=~` и `!~` все равно дают настоящий ответ «да» или «нет».

То же разделение применяется к двум резервным формам. `??` и `? :` может содержать результат неудачного вычисления, когда значение поступает из `value()`, и не может, если речь идет об имени:

| Формула                                  | Результат при `Одометр` не было отправлено |
| ---------------------------------------- | ------------------------------------------ |
| `(одометр + 1) ?? 0`                     | Ничего не производит                       |
| `(value('odometer', 0, 'all') + 1) ?? 0` | `0`                                        |

Простой `??` по имени по-прежнему работает, потому что до этого вычисление не происходит. Только вычисление, обёрнутое внутри `??` или `? :` ведёт себя иначе.

## Явное сравнение значения

Чтобы безопасно использовать в условии значение, которое может быть пустым, сравните его с `== true` или `== false` вместо того чтобы использовать его отдельно. Оба `==` и `!=` Укажите явное «да» или «нет» для пустого значения, чтобы один термин без данных больше не останавливал выполнение всего условия.

Это условие прерывается при любом пакете, в котором отсутствует один из двух атрибутов:

```jexl
door_open || hood_open
```

Это условие продолжает работать и возвращает да, когда открыта любая из дверей:

```jexl
door_open == true || hood_open == true
```

Функции Зона работают так же, потому что они возвращают пустое значение, когда пакет не содержит координат. Это условие прекращает проверку, как только одна из функций не может ответить:

```jexl
leaveGeofence(51577 /* Склад в Остине */) || leaveGeofence(85269 /* Строительная площадка 1 */)
```

Это условие проверяет обе Зона для каждого пакета:

```jexl
leaveGeofence(51577 /* Склад в Остине */) == true || leaveGeofence(85269 /* Стройплощадка 1 */) == true
```

Выберите сравнение, которое соответствует тому, как следует считать пакет без данных:

* `== true` означает «известно, что да». Пустое значение дает `false`, поэтому пакет без данных не подходит.
* `== false` означает «известно, что нет». Пустое значение дает `false`, поэтому пакет без данных не подходит.
* `!= true` означает «неизвестно, что это да». Пустое значение даёт `true`, поэтому пакет без данных тоже совпадает.

{% hint style="warning" %}
Не используйте `!` для значения, которое может быть пустым. `!inGeofence(35229)` ничего не возвращает, когда местоположение неизвестно, поэтому не может обнаружить устройство за пределами Зона. Напишите `inGeofence(35229) == false` для случая «известно, что вне Зона», или `inGeofence(35229) != true` для «неизвестно, что находится внутри».
{% endhint %}

## Откуда берутся пустые значения

Атрибут в формуле принимает пустое значение в следующих случаях:

* Атрибут ни разу не отправлялся этим GPS-устройством.
* Текущий пакет не содержит этот атрибут, хотя в предыдущем пакете он был.
* Позиция в истории, которую вы запросили с помощью `value()` ещё не существует, потому что устройство отправило меньше пакетов, чем требуется для этой позиции.
* Устройство ничего не отправляло более 30 дней, поэтому Navixy удалил сохранённую историю. См. [Полный синтаксис](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node/managing-attributes.md#full-syntax).
* `inGeofence()`, `enterGeofence()`, или `leaveGeofence()` не может ответить, потому что пакет не содержит координат или зона больше не существует в аккаунте. См. [Когда функция зоны не дает ответа](/docs/user/ru/guide/account/iot-logic/nodes/geofence-functions.md#when-a-geofence-function-gives-no-answer).
* `geofenceName()` не имеет названия, которое можно вернуть. См. [Что возвращается](/docs/user/ru/guide/account/iot-logic/nodes/geofence-functions.md#what-you-get-back).

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

## Формулы, которые всегда должны возвращать значение

Защитите вычисление с помощью `!= null`и сначала установите guard. Guard использует `!=`, что дает настоящий ответ «да» или «нет» для пустого значения, поэтому сама проверка никогда не останавливается:

```jexl
value('Одометр', 0, 'all') != null && value('Одометр', 0, 'all') > 100
```

Именно порядок позволяет сторожевому условию работать. Если поменять термины местами, сравнение выполняется раньше сторожевого условия, и всё условие ничего не возвращает.

Чтобы вместо этого подставить резервное значение, используйте `??`. Возвращает правую часть только тогда, когда левая часть пуста:

```jexl
can_speed ?? 0
```

`??` подставляет значение только при отсутствии значения, а не при неудачном вычислении. `(can_speed * 1.1) ?? 0` ничего не возвращает, когда `can_speed` отсутствует, потому что умножение останавливается до `??` того, как до него дойдет дело. Поместите `??` скобки

```jexl
(can_speed ?? 0) * 1.1
```

{% hint style="warning" %}
`?:` и `??` выглядят похоже и ведут себя по-разному. `??` подставляет правую часть только для пустого значения. `?:` подставляет его для пустого значения, а также для `false`, для `0`и для пустого текста. Формула `зажигание ?: true` поэтому возвращает `true` когда зажигание действительно выключено. Используйте `??` когда вы имеете в виду «если значение отсутствует».
{% endhint %}

Вы также можете ограничить вычисление снаружи формулы. Поставьте **IF/THEN Логика** узел перед **Вычисление атрибута** узлом, и проверьте в его условии наличие атрибутов. Подключите путь THEN к вычисляющему узлу, чтобы он получал только пакеты, содержащие значения, которые ему нужны.

Подключите путь ELSE к **Узел передачи данных** узлу тоже. Ветка, которая заканчивается без него, отбрасывает каждый пакет, который до нее доходит, поэтому пакеты, не прошедшие проверку наличия, никогда не покидают поток.

Когда отсутствие значения лучше неправильного, атрибут без значения — правильный результат. Добавляйте резервное значение только тогда, когда системе ниже по потоку нужно значение в каждом пакете.

## Проверьте, не возвращает ли выражение ничего

Исходите из того, что вы видите в [Анализ данных](/docs/user/ru/guide/account/iot-logic/data-stream-analyzer.md):

1. Выберите поток, затем GPS-устройство и вычисленный атрибут.
2. Отключите **Исключить null** переключатель. Неудачное вычисление отображается как `null` значение. Когда переключатель включен, он скрывает эти значения.
3. Увеличьте глубину, если пакет, который вы ищете, старше значений на экране.
4. Сравните атрибут с атрибутами, которые читает его формула. Пакет, в котором входные данные формулы имеют значения, а вычисленный атрибут — `null` указывает на формулу.
5. Проверьте каждый оператор в формуле на соответствие [Как каждый оператор обрабатывает пустое значение](#how-each-operator-handles-an-empty-value).
6. Перепишите каждую часть, которая может быть пустой. Используйте `== true`, `== false`, или `??` резервное значение.

{% hint style="warning" %}
Когда **Исключить null** он включен, последнее значение, которое формула вернула, остается на экране со своей более ранней меткой времени. Показание выглядит актуальным, но относится к более раннему пакету. Выключите переключатель, прежде чем судить, продолжает ли IoT Logic вычислять атрибут.
{% endhint %}

## Часто задаваемые вопросы

#### Пустое значение — это то же самое, что `false`?

Нет. Условие, которое действительно `false` сохраняет `false`, а условие, которое не удалось вычислить, сохраняет пустое значение. Оба отправляют данные через соединение ELSE узла **IF/THEN Логика** узла, поэтому сама ветка не говорит вам, что именно произошло. Последующее выражение, проверяющее атрибут с помощью `my_flag == false` совпадает только с первым случаем.

#### Почему одно GPS-устройство перестало соответствовать условию, которое по-прежнему работает для остальных?

Устройства разных моделей не всегда отправляют одни и те же атрибуты, и одно устройство может перестать передавать атрибут, который остальные по-прежнему отправляют. Тогда условие, которое читает этот атрибут без сравнения, не возвращает ничего только для этого устройства. Сравнивайте каждый термин явно с `== true` или `== false`.

## См. также

* [выражения и синтаксис IF/THEN Логика](/docs/user/ru/guide/account/iot-logic/nodes/logic-node/logic-node-expressions-and-syntax.md#missing-values-and-null-routing) какую ветку выбирает узел Logic и что он сохраняет.
* [Функции зон](/docs/user/ru/guide/account/iot-logic/nodes/geofence-functions.md) для случаев, когда функция зоны не может ответить.
* [Управление атрибутами](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node/managing-attributes.md#expression-language) для `value()` функции и двух вариантов синтаксиса.
* [Примеры вычислений](/docs/user/ru/guide/account/iot-logic/nodes/initiate-attribute-node/calculation-examples.md) для примеров формул.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://navixy.com/docs/user/ru/guide/account/iot-logic/nodes/missing-values-in-expressions.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
