Динамическое построение фильтра RethinkDB

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

var filter = { minAge: 2, maxAge: 11, country:'uk', city':'london'};

В этом случае фильтрация будет:

r.db(dbName).table(tableName)
    .filter(r.row("minAge").ge(filter.minAge)
             .and(r.row("maxAge").le(filter.maxAge))
             .and(r.row('country').eq(filter.country))
             .and(r.row('city').eq(filter.city))
           );

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

var filter2 = { minAge: 2, city':'london'};

Фильтр выше должен привести к следующему

r.db(dbName).table(tableName)
    .filter(r.row("minAge").ge(filter.minAge)             
             .and(r.row('city').eq(filter.city))
           );

Как создать запрос ReQL в соответствии с ключами объекта фильтра, которые я мог бы передать функции фильтра.


person slobodan.blazeski    schedule 11.09.2015    source источник


Ответы (2)


Извините, я неправильно понял и переписываю свой ответ.

Я думаю, что вы можете написать общую функцию, которая запускается на клиенте для возврата запроса для фильтра.

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

{city: 'London', minAge: 12}

Для города мы хотим eq, для minAge мы хотим ge, но, поскольку мы делаем это динамически, гораздо лучше предложить синтаксис для объекта фильтра, подобный этому:

{city: 'London', minAge: ['ge', 12]}

Имея это в виду, я предлагаю следующее решение:

var filterObject = {
  minAge: ['ge', 12],
  city: 'london'
}

r.table('monitor').filter((function(filterObject) {
  var condition

  for (var key in filterObject) {
    var conditionForThisKey
    if (typeof key == 'string' || typeof key == 'number') {
      conditionForThisKey = r.row(key).eq(filterObject[key])
    } else {
      conditionForThisKey = r.row(key)[filterObject[key][0]](filterObject[key][1])
    }

    if (typeof condition === 'undefined') {
      condition = conditionForThisKey
    } else {
      condition = condition.and(conditionForThisKey)
    }
  }

  return condition
})(filterObject))
.run(connection)
.then(function(cursor){ //do stuff with result })
.error(function(err) { //err handling })

Таким образом, мы динамически создаем объект условия для RethinkDB на клиенте. Я думаю, что мы можем передать эту логику в RethinkDB с r.js. Но я не вижу смысла позволять этому работать на RethinkDB, клиент на это способен.

Позвольте мне знать, если это помогает.

person kureikain    schedule 11.09.2015
comment
Спасибо за ответ, но данные всегда присутствуют в строке таблицы, это фильтр, в котором может отсутствовать определенный предикат, что означает, что пользователя не волнует это поле, например, город. - person slobodan.blazeski; 12.09.2015
comment
Да, я понимаю. Поэтому у нас есть филиал. Мы возвращаем true в branch, чтобы сказать, что, если поле не существует, не сравнивайте его, просто верните true. Таким образом, для документа без поля city они эквивалентны: r.row ('minAge'). Ge (filter.minAge) r.row ('minAge'). Ge (filter.minAge) .and (r.branch (r .row.hasField ('city'), r.row ('city'). eq (filter.city)) Используя branch, вы перемещаете логику проверки условия в RethinkDB. Если поле существует, выполните логику сравнения, если поле не существует, ничего не делайте и просто верните true. - person kureikain; 12.09.2015
comment
Простите меня, если мой вопрос не ясен или если я не очень хорошо понимаю ваш ответ, я только вчера начал читать просто переосмыслить, пока отличная книга. Однако мне кажется, что предикат ветки r.row.hasFields проверяет, есть ли в строке таблицы какое-то поле, как я уже говорил, поле, которое может отсутствовать, находится в объекте фильтра. Поэтому, если я использую ветку, это должно быть что-то например r.branch (filter.hasOwnProperty ('city'), r.row ('city'). eq (filter.city), r.expr (true)), и даже если это возможно, я не думаю, что это мудро проверять каждый раз, есть ли у фильтра свойство. - person slobodan.blazeski; 12.09.2015
comment
Виноват. Я совершенно неправильно понимаю ваш вопрос. Я думал, что поле отсутствует в документе, а не в объекте фильтра, как в вашем вопросе. Почему-то я это неправильно понимаю. Позвольте мне немного переосмыслить это и посмотреть, что у нас получилось. - person kureikain; 12.09.2015
comment
Привет, @ slobodan.blazeski, я нашел решение. Надеюсь, у вас это сработает. Дайте мне знать. Прошу прощения за неправильный вопрос. Также спасибо, что понравился Simply RethinkDB. В нем все еще есть много грамматики и синтаксиса, которые нужно исправить. Следите за новостями - person kureikain; 12.09.2015

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

r.db(dbName).table(tableName)
   .filter(r.row("minAge").default(filter.minAge).ge(filter.minAge)
      .and(r.row("maxAge").default(filter.maxAge).le(filter.maxAge))
      .and(r.row('country').default(filter.country).eq(filter.country))
      .and(r.row('city').default(filter.city).eq(filter.city))
   );

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

Обратите внимание, что последовательное сканирование - запросы без использования ограничения на ранней стадии запроса или запросы без каких-либо индексов, с одной только функцией .filter () для всей таблицы - будут использовать последовательные индексы и выполняются МЕДЛЕННО. Он плохо масштабируется.

person analytik    schedule 08.06.2017