2015-08-24 2 views
4

У меня есть следующая таблица «Задачи» в Кассандре.Cassandra - Overlapping Data Ranges

  • TASK_ID UUID - Partition Key
  • Starts_On TIMESTAMP - кластеризация Колонка
  • Ends_On TIMESTAMP - кластеризация Колонка

Я хочу, чтобы выполнить запрос CQL получить пересекающиеся задачи для заданного диапазона дат , Например, если в качестве параметров для запроса я передаю две временные метки (T1 и T2), я хочу получить все задачи, которые применимы в этом диапазоне (то есть перекрывающиеся записи).

Каков наилучший способ сделать это в Кассандре? Я не могу просто использовать два диапазона для Starts_On и Ends_On здесь, потому что для добавления запроса диапазона к Ends_On я должен иметь проверку равенства для Starts_On.

ответ

0

В CQL вы можете задавать запрос только по одному столбцу кластеризации за раз, поэтому вам, вероятно, потребуется выполнить какую-то фильтрацию на стороне клиента в вашем приложении. Таким образом, вы можете задать запрос на start_on, и по мере того, как строки будут возвращены, проверьте end_on в приложении и отбросьте строки, которые вы не хотите.

+0

Спасибо Джим. Но в моей ситуации даже после запроса диапазона на start_on приведет к большому количеству записей, которые мне придется фильтровать на стороне клиента, что я пытаюсь уменьшить. Единственным решением, которое я вижу до сих пор, является наличие другой таблицы с дублирующимися данными, но имеющая end_on в качестве столбца кластеризации, а затем объединение результатов в приложении. –

+0

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

0

Некоторое время назад я написал приложение, столкнувшееся с аналогичной проблемой, при запросе событий, которые имели как начало, так и время окончания. Для нашего сценария мне удалось разделить на userID (как запросы на события конкретного пользователя), установить столбец кластеризации для типа события, а также для даты события. Структура таблицы выглядела примерно так:

CREATE TABLE userEvents (
    userid UUID, 
    eventTime TIMEUUID, 
    eventType TEXT, 
    eventDesc TEXT, 
    PRIMARY KEY ((userid),eventTime,eventType)); 

С этой структурой, я могу запросить по userid и eventtime:

SELECT userid,dateof(eventtime),eventtype,eventdesc FROM userevents 
    WHERE userid=dd95c5a7-e98d-4f79-88de-565fab8e9a68 
    AND eventtime >= mintimeuuid('2015-08-24 00:00:00-0500'); 

userid        | system.dateof(eventtime) | eventtype | eventdesc 
--------------------------------------+--------------------------+-----------+----------- 
dd95c5a7-e98d-4f79-88de-565fab8e9a68 | 2015-08-24 08:22:53-0500 |  End | event1 
dd95c5a7-e98d-4f79-88de-565fab8e9a68 | 2015-08-24 11:45:00-0500 |  Begin |  lunch 
dd95c5a7-e98d-4f79-88de-565fab8e9a68 | 2015-08-24 12:45:00-0500 |  End |  lunch 

(3 rows) 

Этот запрос даст мне все строки событий для конкретного пользователя на сегодняшний день.

ПРИМЕЧАНИЯ:

  • Если вам нужно запросить по ли или нет начала или окончания события (я не сделал), вы хотите заказать eventType впереди eventTime в первичном ключе.
  • Вы будете хранить каждое событие дважды (один раз для начала и один раз для конца). Дублирование данных обычно не вызывает большого беспокойства в Кассандре, но я хотел бы прямо указать на это.
  • В вашем случае вам нужно будет найти хороший ключ для разделения, так как Task_ID будет слишком уникальным (высокая мощность). Это должно быть в Cassandra, так как вы не можете задавать запрос на ключ раздела (только ключ кластеризации).
+0

Спасибо за подробный ответ. Но моя проблема немного отличается, потому что у меня есть два поля timestamp (starts_on и ends_on), которые хранятся в таблице, с которой я хочу запросить. В SQL-мире я бы использовал запросы диапазона в обоих этих полях, но Cassandra не разрешает его кластеризовать столбцы. –

2

Вот еще одна идея (несколько нетрадиционная). Вы можете создать определенную пользователем функцию для реализации фильтра второго диапазона (в Cassandra 2.2 и новее).

Предположит, вы определяете таблицу, как это (показано с Интсами вместо временных меток, чтобы не усложнять пример простым):

CREATE TABLE tasks (
    p int, 
    task_id timeuuid, 
    start int, 
    end int, 
    end_range int static, 
    PRIMARY KEY(p, start)); 

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

CREATE FUNCTION my_end_range(task_id timeuuid, end int, end_range int) 
    CALLED ON NULL INPUT RETURNS timeuuid LANGUAGE java AS 
    'if (end <= end_range) return task_id; else return null;'; 

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

Итак, сначала мы должны установить end_range мы хотим:

UPDATE tasks SET end_range=15 where p=1; 

И скажем, у нас есть эти данные:

SELECT * FROM tasks; 

p | start | end_range | end | task_id 
---+-------+-----------+-----+-------------------------------------- 
1 |  1 |  15 | 5 | 2c6e9340-4a88-11e5-a180-433e07a8bafb 
1 |  2 |  15 | 7 | 3233a040-4a88-11e5-a180-433e07a8bafb 
1 |  4 |  15 | 22 | f98fd9b0-4a88-11e5-a180-433e07a8bafb 
1 |  8 |  15 | 15 | 37ec7840-4a88-11e5-a180-433e07a8bafb 

Теперь давайте получить TASK_ID-х, которые начинаются> = 2 и конец < = 15:

SELECT start, end, my_end_range(task_id, end, end_range) FROM tasks 
    WHERE p=1 AND start >= 2; 

start | end | test.my_end_range(task_id, end, end_range) 
-------+-----+-------------------------------------------- 
    2 | 7 |  3233a040-4a88-11e5-a180-433e07a8bafb 
    4 | 22 |          null 
    8 | 15 |  37ec7840-4a88-11e5-a180-433e07a8bafb 

Так что дает соответствие TASK_ID и вы должны игнор e нулевые строки (я не выяснил способ сброса строк с помощью UDF). Вы заметите, что фильтр start> = 2 упал на одну строку, прежде чем передать его в UDF.

В любом случае, это не идеальный метод, но это может быть то, с чем вы можете работать. :)