2016-08-08 2 views
1

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

Большая часть данных будет структурирована следующим образом (представьте себе следующую схему для миллиардов event структур):

event: struct 
    | 
    +--- timestamp: bigint 
    +--- missing energy: float 
    +--- tracks: array of struct 
    |  | 
    |  +--- momentum: float 
    |  +--- theta angle: float 
    |  +--- hits: array of struct 
    |    | 
    |    +--- detector id: int 
    |    +--- charge: float 
    |    +--- time: float 
    |    +--- ... 
    +--- showers: array of struct 
     | 
     +--- ... 

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

  • импульс дорожки с наибольшим количеством хитов с тета между -2.4 и 2.4
  • средний заряд всех обращений со временем в 0-10 пс на всех направлениях с импульсом больше, чем 10 ГэВ/с
  • средневзвешенная теты из двух дорожек с самым высоким импульсом

и так далее. Что общего у этих запросов, так это то, что все они решаются на один скаляр для каждого события, хотя они вникают в массивы структур, чтобы сделать это. Они выполняют «сокращение» как операции (обычно fold в Scala, aggregate в Spark, DAF в SQL) через фильтрованные, преобразованные подмножества этих массивов. Я мог бы написать их в Scala, как это:

// missing check for when zero tracks passed filter! 
{event => event.tracks      // get list of tracks 
       .filter(abs(_.theta) < 2.4) // in theta range 
       .maxBy(_.hits.size)   // take the one with the most hits 
       .momentum     // return its momentum 
} 

{event => mean(
      event.tracks     // get list of tracks 
       .filter(_.momentum > 10) // in momentum range 
       .flatMap(_.hits)   // explode to hits 
       .filter(_.time < 10)  // in time range 
       .map(_.charge)    // return their charges 
      )}       // ... to the mean function 

// again missing check for less than two tracks! 
{event => val List(one, two) =    // unpack and assign "one" and "two" 
       event.tracks     // get list of tracks 
        .sortBy(_.momentum)  // sort by momentum 
        .take(2)     // take the first two 
      // now compute the weighted mean of structs "one" and "two" 
      (one.theta*one.momentum + two.theta*two.momentum)/
       (one.momentum + two.momentum) 
} 

Почему бы просто не использовать Scala? Моя программа реализована на C и будет работать на графических процессорах. Независимо от того, что я приношу в Scala, это переопределенное подмножество - другими словами, изобретенный язык. (То же самое можно сказать и для Haskell, Javascript или другого языка, который сильно использует функции в качестве аргументов.)

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

Почему бы просто не использовать SQL? Можно ли легко написать такие запросы, как выше, ,, чтобы они были прочитаны кем-либо, кроме автора? Запросы, подобные приведенным выше, являются нормой, а не сложными крайностями.

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

Я полагаю, я мог бы использовать SQL с новыми функциями, как MAXIMAL(collection, function), которые возвращают на структуру из массива, похожий на track[12], но с использованием функции предоставленного пользователем в качестве целевой функции для максимизации, минимизации, находя верх/низ N, и т.д. Я не думаю, что SQL поддерживает передачу функций в качестве аргументов. Если я напишу SQL, то это будет нестандартным.

Существует ли широко используемый диалект SQL, который поддерживает функции передачи в качестве аргументов?

Или есть другой декларативный язык, который я должен рассмотреть?

+1

Ваши вложенные структуры - это просто дополнительные таблицы. У вас есть таблица «event» с уникальным идентификатором. Затем таблица 'track', чем внешний ключ к уникальному идентификатору в' event'. Допустим, что *** одна *** строка 'event' связана с *** нулем для многих строк ***' track'. То же самое относится к 'event':' shower 'и 'track':' hit' и т. Д. И т. Д. Затем SQL обычно становится примером объединения двух таблиц, затем объединения, объединения этого результата в другую таблицу и повторного объединения, и т. д. и т. д. – MatBailie

+0

Что касается 'функций как аргументов', это не будет« нормальным »в любом диалекте SQL. У некоторых есть своя собственная среда CLR и позволяет вам делать какие-то магические вещи, но даже если вы ее заработаете, это будет не стандартный разработчик SQL, который бы узнал * (что касается вас с точки зрения поддержки) *. Но у MS SQL Server есть 'APPLY', который позволяет вам инкапсулировать функции по-другому, которые могут иметь отношение к вам. – MatBailie

+0

Было бы легко написать/легко прочитать, если каждый запрос является объединением + агрегацией? Если вы можете показать, как будут выглядеть SQL-запросы (например, мои три примера), и это не ужасно, это тот ответ, который я ищу. (Извините за субъективность «ужасающего», но я думаю, вы знаете, почему это мой критерий.) –

ответ

1

Я отправил это в комментарий ранее, но перемещение его здесь.

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

Для первого запроса, запрос SPARQL может выглядеть следующим образом:

PREFIX : <http://yournamespace.com/accelerator/> . 

SELECT ?momentum (MAX(?hitcount) as ?maxhits) 
WHERE { 
    SELECT ?momentum (COUNT(?hits) AS ?hitcount) 
    WHERE ?track :momentum ?momentum . 
      ?track :theta ?theta . 
      FILTER (?theta > -2.4 AND ?theta < 2.4) . 
      ?track :hits ?hits 
    GROUP BY ?track 
} 
GROUP BY ?momentum; 

Идентификаторы имеют: префикс на них, потому что они должны быть закодированы как URI. Но это внутренняя деталь для перехода на RDF (формат данных для баз данных SPARQL).

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

+0

Я хочу углубиться в это немного глубже, но до сих пор это выглядит как лучший вариант. RDF и SPARQL удовлетворяют моим критериям «стандарт»: они определены W3C. Этот синтаксис запроса не выглядит слишком обременительным и довольно близок к намерению. XML является более близким к древовидным данным, чем общий граф, хотя мы никогда не будем представлять наши данные в XML по соображениям производительности. –

+0

Вы должны заметить, что RDF не имеет ничего общего с XML. Первоначальный выпуск RDF был вскоре после стандартизации XML, и W3C считал, что использование их собственного «универсального формата сериализации» было хорошей демонстрацией, в которую они верили. Это привело к первоначальному формату сериализации для RDF, являющегося RDF/XML. К сожалению, это имело ** много проблем. Несколько человек в сообществе XML считали, что это подрывает XML, многие люди думали, что RDF привязан к XML, и это был ужасный формат. Рекомендация сегодня заключается в использовании [Terse Triples Language: TurTLe] (https://www.w3.org/TR/turtle/) – PaulaG

+0

У меня есть еще одно обязательство во время бегства Strangeloop, но, пожалуйста, не стесняйтесь отслеживать меня, чтобы говорить о Это. – PaulaG

0

Scala Пример 1 ...

// missing check for when zero tracks passed filter! 
{event => event.tracks      // get list of tracks 
       .filter(abs(_.theta) < 2.4) // in theta range 
       .maxBy(_.hits.size)   // take the one with the most hits 
       .momentum     // return its momentum 
} 

потенциал SQL ....

WITH 
    hit_stats 
AS 
(
    SELECT 
     hit.track_id, 
     COUNT(*) as hit_count 
    FROM 
     hit 
    GROUP BY 
     hit.track_id 
), 
    track_sorted 
AS 
(
    SELECT 
     track.*, 
     ROW_NUMBER() OVER (PARTITION BY track.event_id 
           ORDER BY hit_stats.hit_count DESC 
         ) 
          track_ordinal 
    FROM 
     track 
    INNER JOIN 
     hit_stats 
      ON hit_stats.track_id = track.id 
    WHERE 
      track.theta > -2.4 
     AND track.theta < 2.4 
) 
SELECT 
    * 
FROM 
    event 
INNER JOIN 
    track_sorted 
     ON track_sorted.event_id = event.id 
WHERE 
    track_sorted.track_ordinal = 1 

Или с помощью APPLY из MS SQL Server

SELECT 
    event.*, 
    track.momentum 
FROM 
    event 
OUTER APPLY 
(
    SELECT TOP 1 
     track.*, 
     stat.hit_count 
    FROM 
     track 
    OUTER APPLY 
    (
     SELECT 
      COUNT(*) hit_count 
     FROM 
      hit 
     WHERE 
      track_id = track.id 
    ) 
     stat 
    WHERE 
      track.event_id = event.id 
     AND track.theta > -2.4 
     AND track.theta < 2.4 
    ORDER BY 
     stat.hit_count DESC 
) 
    track 

Это очень вложенными, что Мне труднее читать и поддерживать, чем версия CTE. Но, скорее всего, это будет очень похожий план выполнения.

У Oracle и других диалектов есть другие способы реализации подобных «функций», которые выполняет MS SQL Server с помощью APPLY.

+0

Хорошо, прежде чем продолжить, обратите внимание, что я пытался найти способ вокруг сложного учета, пытаясь вернуться к одному скаляру за каждое событие. Я имею в виду, вы создаете новые таблицы ('hit_stats') и много работаете с' ids', чтобы снова объединить все. –

+0

@JimPivarski - SQL, как вы уже упоминали, декларативный. Выражение * может быть разрешено путем создания таблиц, но в этом случае вы создаете выражение * (Это обозначение называется Common Table Expressions) *. Затем они расширяются, как макрос, в запросе, в котором они используются, и RDBMS затем генерирует план выполнения, чтобы решить объявленную проблему с минимально возможной стоимостью. В основном я просто говорю, что это используется только в качестве помощи для выражения проблемного пространства, а не для выражения решения. Никакие новые таблицы не создаются. – MatBailie

+0

Способ, которым это решение, вероятно, будет планироваться, состоит в том, что каждый трек имеет скалярную операцию, в которой вычисляется количество обращений, путем подсчета записей в индексе. Треки отсортированы, и все, кроме самого высокого ранга, отброшены. Наконец, этот трек присоединяется к его родительскому событию. * (При условии, что количество обращений не изменится, количество обращений может быть кэшировано в самой таблице треков.) * – MatBailie

1

Даже если у вас есть чистые структуры дерева, такие как структуры данных, вы можете посмотреть на базу данных графа. В частности, Neo4j поддерживает декларативный язык запросов, известный как Cypher:

https://neo4j.com/developer/cypher-query-language/

Titan также может быть интересно для масштаба вы говорите, он поддерживает Gremlin из проекта Apache TinkerPop который является мультиплатформенной (но не декларативной):

http://tinkerpop.apache.org/docs/3.0.1-incubating/#preface

+0

Единственная причина, по которой я беспокоюсь о излишнем (представление графа содержит деревья и многое другое), заключается в том, что вам приходится решать нерелевантные концепции в каждом запросе. Преимущество DSL в том, что он несколько упрощен. Я смотрел на Cypher и Gremlin и думал о том, как будут выглядеть эти запросы: похоже, вам нужно будет сказать, что события содержат треки, когда вы хотите получить конкретный трек из события.Но события _always_ содержат дорожки ... –

Смежные вопросы