Spec-Zone.ru › Elasticsearch 7
›Elasticsearch Guide [7.17] ›Агрегирования ›Агрегирования по корзинам

Агрегирование значимых терминов

Агрегирование, которое возвращает интересные или необычные появления терминов в наборе.

Примеры вариантов использования:

  • Предложение "H5N1" при поиске пользователей по запросу "птичий грипп" в тексте
  • Определение продавца, который является «общей точкой компрометации», из истории транзакций владельцев кредитных карт, сообщивших о потере
  • Предложение ключевых слов, относящихся к символу акций $ATI, для автоматического классификатора новостей
  • Выявление мошеннического врача, который ставит диагноз о повреждениях шейного отдела позвоночника чаще, чем положено
  • Выявление производителя шин, у которого наблюдается непропорционально большое число проколов

Во всех этих случаях выбираемые термины не являются просто самыми популярными терминами в наборе. Это термины, которые претерпели значительные изменения в популярности, измеренные между набором фонового и переднего плана. Если термин «H5N1» существует только в 5 документах в индексе из 10 миллионов документов, а также встречается в 4 из 100 документов, составляющих результаты поиска пользователя, это значимо и, вероятно, очень актуально для их поиска. 5/10 000 000 по сравнению с 4/100 — это большой скачок частоты.

Анализ одного набора

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

Пример:

GET /_search
{
  "query": {
    "terms": { "force": [ "British Transport Police" ] }
  },
  "aggregations": {
    "significant_crime_types": {
      "significant_terms": { "field": "crime_type" }
    }
  }
}

Ответ:

{
  ...
  "aggregations": {
    "significant_crime_types": {
      "doc_count": 47347,
      "bg_count": 5064554,
      "buckets": [
        {
          "key": "Bicycle theft",
          "doc_count": 3640,
          "score": 0.371235374214817,
          "bg_count": 66799
        }
              ...
      ]
    }
  }
}

При запросе к индексу всех преступлений всех полицейских управлений эти результаты показывают, что британская транспортная полиция выделяется как подразделение, в котором наблюдается непропорционально большое количество краж велосипедов. Обычно кражи велосипедов составляют только 1% преступлений (66799/5064554), но для британской транспортной полиции, которая занимается преступлениями на железных дорогах и станциях, кражи велосипедов составляют 7% преступлений (3640/47347). Это значительное увеличение частоты в семь раз, поэтому эта аномалия была выделена как самый распространенный тип преступлений.

Проблема использования запроса для обнаружения аномалий заключается в том, что он предоставляет нам только один подмножество для сравнения. Чтобы обнаружить все другие аномалии полицейских управлений, нам необходимо повторить запрос для каждого из различных управлений.

Это может быть трудоемким способом поиска необычных шаблонов в индексе.

Анализ нескольких наборов

Более простой способ выполнения анализа по нескольким категориям — использование агрегирования на уровне родительского элемента для сегментации данных перед анализом.

Пример использования родительского агрегирования для сегментации:

GET /_search
{
  "aggregations": {
    "forces": {
      "terms": { "field": "force" },
      "aggregations": {
        "significant_crime_types": {
          "significant_terms": { "field": "crime_type" }
        }
      }
    }
  }
}

Ответ:

{
 ...
 "aggregations": {
    "forces": {
        "doc_count_error_upper_bound": 1375,
        "sum_other_doc_count": 7879845,
        "buckets": [
            {
                "key": "Metropolitan Police Service",
                "doc_count": 894038,
                "significant_crime_types": {
                    "doc_count": 894038,
                    "bg_count": 5064554,
                    "buckets": [
                        {
                            "key": "Robbery",
                            "doc_count": 27617,
                            "score": 0.0599,
                            "bg_count": 53182
                        }
                        ...
                    ]
                }
            },
            {
                "key": "British Transport Police",
                "doc_count": 47347,
                "significant_crime_types": {
                    "doc_count": 47347,
                    "bg_count": 5064554,
                    "buckets": [
                        {
                            "key": "Bicycle theft",
                            "doc_count": 3640,
                            "score": 0.371,
                            "bg_count": 66799
                        }
                        ...
                    ]
                }
            }
        ]
    }
  }
}

Теперь мы можем обнаруживать аномалии для каждого из полицейских управлений с помощью одного запроса.

Мы можем использовать другие виды агрегирования на верхнем уровне для сегментации данных, например, сегментации по географическому региону для выявления необычных «горячих точек» определенного типа преступлений:

GET /_search
{
  "aggs": {
    "hotspots": {
      "geohash_grid": {
        "field": "location",
        "precision": 5
      },
      "aggs": {
        "significant_crime_types": {
          "significant_terms": { "field": "crime_type" }
        }
      }
    }
  }
}

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

  • В аэропортах наблюдается необычно большое количество изъятых оружия
  • В университетах наблюдается увеличение краж велосипедов

На более высоком уровне масштабирования geohash_grid с более обширными зонами покрытия мы начнем видеть, где все управление полиции может бороться с необычно большим объемом определенного типа преступлений.

Очевидно, сегментация на основе времени на верхнем уровне поможет выявить текущие тенденции для каждого момента времени, где простое агрегирование terms обычно показывает очень популярные «константы», которые сохраняются во всех временных интервалах.

Как рассчитываются оценки?

Числа, возвращаемые для оценок, предназначены в первую очередь для разумного ранжирования различных предложений, а не для того, чтобы быть легко понятными конечным пользователям. Оценки выводятся из частоты документов в наборах переднего плана и фонового. Вкратце, термин считается значимым, если существует заметное различие в частоте, с которой термин появляется в подмножестве и в фоновом наборе. Способ ранжирования терминов можно настроить, см. раздел «Параметры».

Использование в свободных текстовых полях

Агрегирование significant_terms можно эффективно использовать в токенизированных свободных текстовых полях для предложения:

  • ключевых слов для уточнения поисковых запросов конечных пользователей
  • ключевых слов для использования в запросах percolator

Выбор свободного текстового поля в качестве объекта анализа значимых терминов может быть дорогостоящим! Он будет пытаться загрузить каждое уникальное слово в ОЗУ. Рекомендуется использовать его только в небольших индексах.

Используйте шаблон «похоже на это, но не на это»

Вы можете выявить неправильно классифицированный контент, сначала выполнив поиск в структурированном поле, например, category:adultMovie, и используя significant_terms в свободном текстовом поле «movie_description». Возьмите предложенные слова (я оставлю их вашему воображению) и затем выполните поиск по всем фильмам, не помеченным как category:adultMovie, но содержащим эти ключевые слова. Теперь у вас есть упорядоченный список плохо классифицированных фильмов, которые вы должны переклассифицировать или, по крайней мере, удалить из категории «familyFriendly».

Значение значимости каждого термина также может предоставить полезную boost настройку для сортировки совпадений. Использование параметра minimum_should_match запроса terms с ключевыми словами поможет контролировать баланс точности/полноты в наборе результатов. Высокое значение будет иметь небольшое количество релевантных результатов, заполненных ключевыми словами, а значение «1» даст более полный набор результатов со всеми документами, содержащими любое ключевое слово.

Отобразите значимые термины в контекстеСвободные текстовые значимые термины гораздо легче понять, если посмотреть их в контексте. Возьмите результаты предложений significant_terms из свободного текстового поля и используйте их в запросе terms по тому же полю с клаузой highlight, чтобы представить пользователям примеры фрагментов документов. Если термины представлены без сглаживания, выделенные, с правильным регистром, в правильном порядке и с контекстом, их значимость/смысл более очевидны.

Настраиваемые фоновые наборы

Обычно набор документов переднего плана «дифференцируется» по отношению к фоновому набору всех документов в вашем индексе. Однако иногда может оказаться полезным использование более узкого фонового набора в качестве основы для сравнения. Например, запрос по документам, относящимся к «Мадриду», в индексе с контентом со всего мира может показать, что «испанский» — это значимый термин. Это может быть правдой, но если вы хотите более конкретные термины, вы можете использовать background_filter для термина Испания, чтобы установить более узкий набор документов в качестве контекста. С этим фоновым набором «испанский» теперь будет рассматриваться как обычное явление и, следовательно, не будет столь значимым, как слова вроде «столица», которые сильнее связаны с Мадридом. Обратите внимание, что использование фильтра фонового набора замедлит работу — теперь для каждой частоты термина в фоновом наборе необходимо выводить данные на лету из фильтрации списков, а не читать предварительно вычисленную подсчетную информацию в индексе для термина.

Ограничения

Значимые термины должны быть индексированными значениями

В отличие от агрегации терминов, в настоящее время невозможно использовать сгенерированные скриптом термины для подсчета. Из-за того, как агрегация significant_terms должна учитывать частоты как фоновых, так и передних планов, использование скрипта на всем индексе для получения частот фона для сравнения было бы чрезмерно затратно. Также по аналогичным причинам DocValues не поддерживаются в качестве источников данных о терминах.

Отсутствует анализ полей с плавающей точкой

Поля с плавающей точкой в настоящее время не поддерживаются в качестве объекта анализа significant_terms. Хотя поля целого или длинного типа могут использоваться для представления таких понятий, как номера счетов или номера категорий, которые могут быть интересны для отслеживания, поля с плавающей точкой обычно используются для представления количеств чего-либо. Поэтому отдельные термины с плавающей точкой не подходят для этого вида анализа частот.

Использование в качестве родительской агрегации

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

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

Приблизительные подсчеты

Подсчеты количества документов, содержащих термин, предоставленные в результатах, основаны на суммировании выборок, возвращенных каждым фрагментом, и поэтому могут быть:

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

Как и большинство решений по проектированию, это основа компромисса, в котором мы выбрали быструю производительность в ущерб некоторым (как правило, небольшим) неточностям. Тем не менее, настройки size и shard size, рассматриваемые в следующем разделе, предоставляют инструменты для контроля уровней точности.

Параметры

Оценка JLH

Оценка JLH может быть использована в качестве оценки значимости, добавив параметр

	 "jlh": {
	 }

Оценки получены из частот документов в наборах фоновый и передний. Абсолютное изменение популярности (foregroundPercent - backgroundPercent) будет отдавать предпочтение общим терминам, в то время как относительное изменение популярности (foregroundPercent/ backgroundPercent) — редким терминам. Редкие и общие термины в сущности представляют баланс точности и полноты, поэтому абсолютные и относительные изменения умножаются, чтобы обеспечить золотую середину между точностью и полнотой.

Взаимная информация

Взаимная информация, как описано в «Поиске информации», Manning и др., Глава 13.5.1, может быть использована в качестве оценки значимости, добавив параметр

	 "mutual_information": {
	      "include_negatives": true
	 }

Взаимная информация не различает термины, которые являются описательными для подмножества или для документов за пределами подмножества. Значимые термины, следовательно, могут содержать термины, которые появляются более или менее часто в подмножестве, чем за его пределами. Чтобы отфильтровать термины, которые появляются реже в подмножестве, чем в документах за пределами подмножества, можно установить include_negatives на false.

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

"background_is_superset": false

Критерий хи-квадрат

Критерий хи-квадрат, как описано в «Поиске информации», Manning и др., Глава 13.5.2, может быть использован в качестве оценки значимости, добавив параметр

	 "chi_square": {
	 }

Критерий хи-квадрат ведет себя как взаимная информация и может быть настроен с теми же параметрами include_negatives и background_is_superset.

Нормализованное расстояние Google

Нормализованное расстояние Google, как описано в "Расстояние Google Similarity", Cilibrasi и Vitanyi, 2007, может быть использовано в качестве оценки значимости, добавив параметр

	 "gnd": {
	 }

gnd также принимает параметр background_is_superset.

Оценка p-значения

P-значение — это вероятность получения результатов теста, по крайней мере, столь же экстремальных, как фактически наблюдаемые, при предположении, что нулевая гипотеза верна. P-значение вычисляется в предположении, что набор переднего плана и набор фона независимы испытания Бернулли, с нулевой гипотезой, что вероятности одинаковы.

Пример использования

В этом примере вычисляется оценка p-значения для терминов user_agent.version, учитывая набор переднего плана «закончился неудачей» против «НЕ закончился неудачей».

"background_is_superset": false указывает, что фоновый набор не содержит подсчётов набора переднего плана, так как они отфильтрованы.

"normalize_above": 1000 способствует возвращению согласованных результатов значимости на различных масштабах. 1000 указывает, что счётчики терминов, превышающие 1000, масштабируются вниз на коэффициент 1000/term_count.

GET /_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "event.outcome": "failure"
          }
        },
        {
          "range": {
            "@timestamp": {
              "gte": "2021-02-01",
              "lt": "2021-02-04"
            }
          }
        },
        {
          "term": {
            "service.name": {
              "value": "frontend-node"
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "failure_p_value": {
      "significant_terms": {
        "field": "user_agent.version",
        "background_filter": {
          "bool": {
            "must_not": [
              {
                "term": {
                  "event.outcome": "failure"
                }
              }
            ],
            "filter": [
              {
                "range": {
                  "@timestamp": {
                    "gte": "2021-02-01",
                    "lt": "2021-02-04"
                  }
                }
              },
              {
                "term": {
                  "service.name": {
                    "value": "frontend-node"
                  }
                }
              }
            ]
          }
        },
        "p_value": {"background_is_superset": false, "normalize_above": 1000}
      }
    }
  }
}

Процент

Простая вычисление количества документов в выборке переднего плана с термином, делённое на количество документов в фоновом с этим термином. По умолчанию это даёт оценку больше нуля и меньше единицы.

Преимущество этого эвристического метода заключается в простоте его объяснения любому, кто знаком со статистикой «на душу населения». Однако в областях с высокой размерностью существует тенденция отбора самых редких терминов, таких как опечатки, которые встречаются только один раз, потому что они набирают 1/1 = 100%.

Опытному боксёру было бы сложно выиграть чемпионат, если бы приз присуждался только на основании процента выигранных боёв — по этим правилам новичка с только одним боем под его поясом было бы невозможно победить. Для подкрепления мнения обычно требуется несколько наблюдений, поэтому в этих случаях рекомендуется установить как min_doc_count, так и shard_min_doc_count на более высокое значение, например, 10, чтобы отфильтровать термины с низкой частотой, которые в противном случае имеют приоритет.

	 "percentage": {
	 }

Какой лучше?

Грубо говоря, mutual_information отдаёт предпочтение терминам с высокой частотой, даже если они также часто встречаются и в фоновом наборе. Например, в анализе текстов естественного языка это может привести к выбору стоп-слов. mutual_information маловероятно выберет очень редкие термины, такие как опечатки. gnd отдаёт предпочтение терминам с высокой частотой совместного появления и избегает выбора стоп-слов. Это может быть лучше для обнаружения синонимов. Однако, gnd имеет тенденцию выбирать очень редкие термины, которые, например, являются результатом опечатки. chi_square и jlh находятся где-то посередине.

Трудно сказать, какой из различных эвристических методов окажется лучшим выбором, так как это зависит от того, для чего используются значимые термины (см., например, Yang и Pedersen, «Сравнительное исследование выбора признаков в категоризации текста», 1997, для изучения использования значимых терминов для выбора признаков для классификации текста).

Если ни один из вышеперечисленных показателей не подходит для вашего случая, то другой вариант — реализовать пользовательскую меру значимости:

Скриптированный

Настраиваемые оценки могут быть реализованы с помощью скрипта:

	    "script_heuristic": {
              "script": {
	        "lang": "painless",
	        "source": "params._subset_freq/(params._superset_freq - params._subset_freq + 1)"
	      }
            }

Скрипты могут быть встроенными (как в примере выше), индексированными или храниться на диске. Подробности об этих вариантах см. в документации по скриптам.

Доступные параметры в скрипте:

_subset_freq

Количество документов, в которых появляется термин в подмножестве.

_superset_freq

Количество документов, в которых появляется термин в супермножестве.

_subset_size

Количество документов в подмножестве.

_superset_size

Количество документов в супермножестве.

Размер и размер фрагмента

Параметр size может быть установлен для определения количества корзин с терминами, которые должны быть возвращены из общего списка терминов. По умолчанию узел, координирующий процесс поиска, запросит каждый фрагмент предоставить свои собственные верхние корзины с терминами, и после ответа всех фрагментов он сведет результаты к окончательному списку, который затем будет возвращен клиенту. Если количество уникальных терминов больше size, возвращаемый список может быть немного неточным и неверным (может быть, что количество терминов немного неточно, и даже может оказаться, что термин, который должен был быть в верхних корзинках размера, не был возвращён).

Для обеспечения большей точности используется кратное окончательному значению size в качестве количества терминов, которые необходимо запросить у каждого фрагмента (2 * (size * 1.5 + 10)). Для ручного управления этим параметром можно использовать параметр shard_size, чтобы контролировать объёмы кандидатов в термины, генерируемые каждым фрагментом.

Термины с низкой частотой могут оказаться наиболее интересными после объединения всех результатов, поэтому агрегация значимых терминов может дать результаты более высокого качества, когда параметр shard_size устанавливается на значения, значительно превышающие параметр size. Это гарантирует, что большее количество перспективных кандидатов в термины будут объединены узлом сокращения перед окончательным выбором. Очевидно, что большие списки кандидатов в термины приведут к дополнительному трафику сети и использованию памяти, поэтому это компромисс «качество/стоимость», который необходимо сбалансировать. Если shard_size установлен на -1 (по умолчанию), то shard_size будет автоматически оцениваться на основе количества фрагментов и параметра size.

shard_size не может быть меньше size (так как это не имеет смысла). Если это так, Elasticsearch переопределит его и сбросит на значение, равное size.

Минимальное количество документов

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

GET /_search
{
  "aggs": {
    "tags": {
      "significant_terms": {
        "field": "tag",
        "min_doc_count": 10
      }
    }
  }
}

В приведенной выше агрегации будут возвращены только те теги, которые встречаются 10 или более раз. Значение по умолчанию равно 3.

Термины, получившие высокие оценки, будут собраны на уровне фрагмента и объединены с терминами, собранными из других фрагментов на втором этапе. Однако фрагмент не имеет информации о глобальных частотах терминов. Решение о добавлении термина в список кандидатов зависит только от оценки, вычисленной на фрагменте с использованием локальных частот фрагмента, а не глобальных частот слова. Критерий min_doc_count применяется только после объединения локальных статистических данных терминов всех фрагментов. Таким образом, решение о добавлении термина в качестве кандидата принимается, не имея полной уверенности в том, что термин фактически достигнет требуемого значения min_doc_count. Это может привести к тому, что многие термины с высокой (глобальной) частотой будут отсутствовать в конечном результате, если в списки кандидатов попадут термины с низкой частотой, но высокой оценкой. Чтобы избежать этого, параметр shard_size можно увеличить, чтобы разрешить больше терминов-кандидатов на фрагментах. Однако это увеличивает потребление памяти и сетевой трафик.

shard_min_doc_count

Параметр shard_min_doc_count регулирует уверенность фрагмента в том, что термин следует добавить в список кандидатов относительно min_doc_count. Термины будут рассматриваться только в том случае, если их локальная частота на фрагменте в заданном наборе выше, чем shard_min_doc_count. Если ваш словарь содержит много терминов с низкой частотой, и вы не заинтересованы в них (например, в ошибках написания), то вы можете установить параметр shard_min_doc_count, чтобы отфильтровать термины-кандидаты на уровне фрагмента, которые с достаточной уверенностью не достигнут требуемого значения min_doc_count даже после объединения локальных подсчётов. Параметр shard_min_doc_count по умолчанию установлен на значение 0 и не имеет эффекта, если его не задать явно.

Установка параметра min_doc_count на значение 1 не рекомендуется, так как это приводит к возврату терминов, которые являются опечатками или другими странностями. Нахождение более одного вхождения термина помогает усилить то, что, хотя это всё ещё редко, термин не был результатом случайной ошибки. Значение по умолчанию 3 используется для обеспечения минимального уровня доказательств. Установка параметра shard_min_doc_count слишком высоким приведёт к значительной фильтрации терминов-кандидатов на уровне фрагмента. Это значение должно быть установлено намного ниже, чем значение min_doc_count/#shards.

Пользовательский контекст фона

По умолчанию источником статистической информации о частотах фоновых терминов является весь индекс, и этот охват может быть сужен с помощью background_filter, чтобы сфокусироваться на значимых терминах в более узком контексте:

GET /_search
{
  "query": {
    "match": {
      "city": "madrid"
    }
  },
  "aggs": {
    "tags": {
      "significant_terms": {
        "field": "tag",
        "background_filter": {
          "term": { "text": "spain" }
        }
      }
    }
  }
}

Приведенный выше фильтр поможет сфокусироваться на терминах, характерных для города Мадрид, а не на таких терминах, как "испанский", которые являются необычными в глобальном контексте всего индекса, но распространёнными в подмножестве документов, содержащих слово "Испания".

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

Фильтрация значений

Возможна (хотя и редко необходима) фильтрация значений, для которых будут созданы ведра. Это можно сделать с помощью параметров include и exclude, которые основаны на регулярных выражениях или массивах точных терминов. Эта функциональность соответствует функциям, описанным в документации по агрегации terms.

Режим сбора

Для предотвращения проблем с памятью, агрегация significant_terms всегда вычисляет дочерние агрегации в режиме breadth_first. Описание различных режимов сбора можно найти в документации по агрегации terms.

Подсказка выполнения

Существует несколько механизмов выполнения агрегаций по терминам:

  • Использование значений поля напрямую для агрегации данных по ведру (map)
  • Использование глобальных порядковых номеров поля и выделение одного ведра на каждый глобальный порядковый номер (global_ordinals)

Elasticsearch старается использовать разумные значения по умолчанию, поэтому обычно настройка этого не требуется.

global_ordinals — это значение по умолчанию для поля keyword. Оно использует глобальные порядковые номера для динамического выделения ведер, поэтому потребление памяти линейно зависит от количества значений документов, которые входят в область применения агрегации.

map следует учитывать только в случае, если очень мало документов соответствуют запросу. В противном случае, режим выполнения на основе порядковых номеров значительно быстрее. По умолчанию, map используется только при выполнении агрегации на скриптах, так как они не имеют порядковых номеров.

GET /_search
{
  "aggs": {
    "tags": {
      "significant_terms": {
        "field": "tags",
        "execution_hint": "map" 
      }
    }
  }
}

Возможные значения — map, global_ordinals

Обратите внимание, что Elasticsearch проигнорирует эту подсказку выполнения, если она не применима.

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-aggregations-bucket-significantterms-aggregation.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API