Эффективный запрос Datomic для фильтрации наборов с разбивкой на страницы

Учитывая, что Datomic не поддерживает нумерацию страниц, мне интересно, как эффективно поддерживать такой запрос, как:

Возьмите первые 30 объектов на :history/body, найдите объекты, :history/body которых соответствует некоторому регулярному выражению.

Вот как я бы выполнял сопоставление регулярных выражений в одиночку:

{:find [?e]
 :where [[?e :history/body ?body]
         [(re-find #"foo.*bar$" ?body)]]}

Наблюдения:

  1. Затем я мог бы (take ...) из них, но это не то же самое, что сопоставление с первыми 30 объектами.
  2. Я мог бы получить все объекты, take 30 а затем вручную отфильтровать с помощью re-find, но если у меня есть 30 миллионов объектов, получение всех их только до take 30 кажется дико неэффективным. Кроме того: что, если я хочу взять 20 миллионов из моих 30 миллионов сущностей и отфильтровать их через re-find?

Документы Datomic говорят о том, как запросы выполняются локально, но я пытался выполнять преобразования в памяти для набора из 52913 сущностей (конечно, они полностью touched), и это занимает ~ 5 секунд. Представьте, как плохо это будет в миллионах или десятках миллионов.


person devth    schedule 26.09.2014    source источник


Ответы (1)


(Просто мозговой штурм, здесь)

Прежде всего, если вы когда-либо использовали регулярное выражение, вы можете рассмотреть возможность полнотекстового индекса в :history/body, чтобы вы могли сделать:

[(fulltext $ :history/body "foo*bar") [[?e]]]

(Примечание. Вы не можете изменить :db/fulltext true/false в существующей схеме объекта)

Сортировка — это то, что вам нужно делать вне запроса. Но в зависимости от ваших данных вы можете ограничить свой запрос одной «страницей», а затем применить свой предикат только к этим объектам.

Например, если бы мы разбивали на страницы только :history объектов с помощью автоинкрементного :history/id, то мы бы заранее знали, что «Страница 3» — это :history/id от 61 до 90.

[:find ?e
 :in $ ?min-id ?max-id
 :where
 [?e :history/id ?id]
 (<= ?min-id ?id ?max-id)
 (fulltext $ :history/body "foo*bar") [[?e]]]

Может быть, что-то вроде этого:

(defn get-filtered-history-page [page-n match]
  (let [per-page 30
        min-id (inc (* (dec page-n) per-page))
        max-id (+ min-id per-page)]
    (d/q '[:find ?e
           :in $ ?min-id ?max-id ?match
           :where
           [?e :history/id ?id]
           [(<= ?min-id ?id ?max-id)]
           [(fulltext $ :history/body ?match) [[?e]]]]
      (get-db) min-id max-id match)))

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

person Community    schedule 07.10.2014
comment
Спасибо. Я проработаю это, когда у меня будет шанс, похоже, хорошее начало. - person devth; 08.10.2014