Замена нескольких объединений в SQL представлениями CouchDB

Я реализую функцию фильтра для своего приложения и испытываю проблемы с записью представления на CouchDB. В SQL это будет оператор с множественным объединением. Как заменить множественное объединение в CouchDB. Эта статья посвящена одиночному объединению: http://www.cmlenz.net/archives/2007/10/couchdb-joins. Однако для меня не очевидно, как расширить этот подход для множественного объединения.

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

Мое приложение должно поддерживать несколько фильтров AND. Например, найдите все сеансы, где местоположение ("Сиэтл", "Нью-Йорк") И начало эпохи>= 1336608413. И мне также нужно сохранить настройки фильтра.

В SQL у меня есть две таблицы: Session и Filter. Затем у меня есть одно соединение на условие фильтра между сеансом и таблицей фильтров, чтобы получить все фильтрованные сеансы.

Session Table
location    start-epoch    
Seattle     1336608413     

Filter Table
Name        Value
location    Seattle
location    New York
start-epoch 1336608413     


Query:
select * 
from Session s 
where exists (select 1 from Filter f where f.name = 'location' and f.value = s.location)
     and exists (select 1 from Filter f on f.name = 'start-epoch' and s.start-epoch >= f2.value)

В CouchDB:

{"type":"session", "location":"Seattle", "start-epoch":1336608413}

Тогда как смоделировать фильтр и написать представление?

Фильтр может быть примерно таким:

{"type":"filter", "location":["Seattle", "New York"], "start-epoch":1336608413}

Тогда как написать вид?

Я хочу добиться этого внутри CouchDB. Я надеюсь создать представление с именем FilteredSessions и получить отфильтрованный сеанс за один HTTP-вызов Couch. Причина в том, что у меня 5 разных клиентов (iOS, Android, Silverlight, Ruby on Rails и Windows 8), и я не хочу переписывать логику 5 раз. Если я использую sqlite, я могу создать представление под названием "FilteredView" для достижения этой цели. Я надеюсь написать эквивалент в карте уменьшить.

3 ответа

Решение

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

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

Допустим, у вас есть около 30 сессионных документов. У них есть два свойства: местоположение и начало эпохи. Например:

{
 "_id"         : "f80hqwfe8nfaodnia9dgs8n9",
 "location"    : "Seattle",
 "start-epoch" : "1336608413",
 "type"        : "session"
}

Или же...

{
 "_id"         : "f8fsdnio345235aodnia9dgs8n9",
 "location"    : "New York",
 "start-epoch" : "1236630174",
 "type"        : "session"
}

Тогда у вас есть несколько фильтров, например:

{
 "_id"         : "f80hqwfe8nfaodnia9dgs8n9",
 "location"    : "New York",
 "type"        : "filter"
}

Или же...

{
 "_id"         : "f80hqwfe8nfaodnia9dgs8n9",
 "start-epoch" : "1336608413",
 "type"        : "filter"
}

Вы можете вывести все сеансы в одно представление:

"sesssions": {
  "map": "function (doc) {
    if (doc.type === "session") {
      emit(doc.start-epoch, doc);
    }
}"

Затем перетащите все фильтры в одно представление (и вы можете обновить это на лету):

"filters": {
  "map": "function (doc) {
    if (doc.type === "filter") {
      emit(doc.type, doc);
    }
}"

Тогда я собрал бы свой последний "вид" в nodejs. Я хотел бы сделать запрос к представлению фильтров моей БД или создать новый фильтр, если это необходимо, и сохранить его в переменной, например, в фильтрах или фильтрах. Затем я сделал бы еще один вызов для просмотра сессий моего БД и перетащил эти данные в переменную, например, сессии.

Небольшое примечание здесь. Вам нужно вызвать представление (скажем, вы сохранили объект json, который couch возвратил в представление var), а затем сохранить view.rows в переменной session, которая выдаст вам массив сессий, подобный этому:

[
 {
  "_id"         : "f80hqwfe8nfaodnia9dgs8n9",
  "location"    : "Seattle",
  "start-epoch" : "1336608413",
  "type"        : "session"
 },
 {
  "_id"         : "f8fsdnio345235aodnia9dgs8n9",
  "location"    : "New York",
  "start-epoch" : "1236630174",
  "type"        : "session"
 }
]

Тогда просто делай

var myFinalView = sessions.forEach(function (session) {
  if (filter.location !== session.location) {
    // session.splice or delete session
  }
  return;
})

И тогда myFinalView будет нужным вам объектом JSON. Я надеюсь, что в этом есть смысл. Пожалуйста, попросите меня уточнить, если это будет необходимо.

var sessions = [];
var filters = [];

function(doc) {
  if (doc.type === 'session') {
    sessions.push(doc);
  }
  if (doc.type === 'filter') {
    filters.push(doc);
  }
  emit(filters.length, filters);
  emit(sessions.length, sessions);
}

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

Спасибо, что поделились этим вызовом! Этот действительно толкает конверт на CouchDB. Я надеюсь, что мы можем понять это, это должно быть возможно.

Другие вопросы по тегам