2016-06-09 2 views
2

Предположим, что я создаю базу данных Cloudant для хранения всех записей службы для моего парка автомобилей (я не, но проблема почти такая же.) для того, чтобы сделать это, у меня есть два типа записей:(Cloudant) Создание представления для объединения двух типов документов

Автомобили:

{ 
    "type": "Car", 
    "_id": "VIN 1", 
    "plateNumber": "ecto-1", 
    "plateState": "NY", 
    "make": "Cadillac", 
    "model": "Professional Chassis", 
    "year": 1959 
    } 

    { 
    "type": "Car", 
    "_id": "VIN 2", 
    "plateNumber": "mntclmbr", 
    "plateState": "VT", 
    "make": "Jeep", 
    "model": "Wrangler", 
    "year": 2016 
    } 

и обслуживание записей:

{ 
    "type": "ServiceRecord", 
    "_id": "service1", 
    "carServiced": "VIN 1", 
    "date": [1984, 6, 8], 
    "item": "Cleaning (Goo)", 
    "cost": 300 
    } 

    { 
    "type": "ServiceRecord", 
    "_id": "service2", 
    "carServiced": "VIN 1", 
    "date": [1984, 6, 9], 
    "item": "Cleaning (Marshmellow)", 
    "cost": 800 
    } 

    { 
    "type": "ServiceRecord", 
    "_id": "service3", 
    "carServiced": "VIN 2", 
    "date": [2016, 4, 2], 
    "item": "Alignment", 
    "cost": 150 
    } 

Там пара вещей, чтобы отметить о том, как это работает:

  • Номер VIN автомобиля никогда не будет изменен, используется как документ _id.
  • Сервисные записи для автомобиля не должны быть потеряны, если автомобиль зарегистрирован в новом состоянии или с новым номером пластины.
  • Из-за объема автомобилей и того, как часто они нуждаются в ремонте, нецелесообразно редактировать документ автомобиля, если необходимо добавить, удалить или изменить служебную запись.

В настоящее время у меня есть пара видов для поиска информации.

Во-первых, я получил карту от номерного знака к VIN:

function(doc){ 
    if (doc.type == "Car"){ 
     emit([doc.plateState, doc.plateNumber], doc._id); 
    } 
} 

// Results in: 
["NY", "ecto-1"] -> "VIN 1" 
["VT", "mntclmbr"] -> "VIN 2" 

Во-вторых, я получил карту карту от VINs всех автомобилей на услугу записей:

function(doc){ 
    if (doc.type == "ServiceRecord"){ 
     emit(doc.carServiced, doc); 
    } 
} 

// Results in: 
"VIN 1" -> {"_id": "service1", ...} 
"VIN 1" -> {"_id": "service2", ...} 
"VIN 2" -> {"_id": "service3", ...} 

Наконец, я получил карту карты от VINs всех болидов и дат обслуживания для конкретной услуги, что произошло в тот же день:

function(doc){ 
    if (doc.type == "ServiceRecord"){ 
     var key = [doc.carServiced, doc.date[0], doc.date[3], doc.date[2]]; 
     emit(key, doc); 
    } 
} 

// Results in: 
["VIN 1", 1984, 6, 8] -> {"_id": "service1", ...} 
["VIN 1", 1984, 6, 9] -> {"_id": "service2", ...} 
["VIN 2", 2016, 4, 2] -> {"_id": "service3", ...} 

С этими тремя картами я могу найти три разных вещи:

  • VIN любого автомобиля по номеру.
  • Сервисные записи любого автомобиля по VIN.
  • Сервисные записи любого автомобиля по VIN для любого года, месяца или дня.

Однако не может найти все служебные записи автомобиля на номерном знаке. (. По крайней мере, не в один шаг) Чтобы сделать это, я должен был бы карту, как это:

["NY", "ecto-1"] -> {"_id": "service1", ...} 
["NY", "ecto-1"] -> {"_id": "service2", ...} 
["VT", "mntclmbr"] -> {"_id": "service3", ...} 

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

["NY", "ecto-1", 1984, 6, 8] -> {"_id": "service1", ...} 
["NY", "ecto-1", 1984, 6, 9] -> {"_id": "service2", ...} 
["VT", "mntclmbr", 2016, 4, 2] -> {"_id": "service3", ...} 

к сожалению, я не знаю, как создавать карты, как это потому, что ключ требует информации из двух документов. Я могу получать информацию о таре из документов Car, и я могу получить только служебную информацию (включая документ _id для значения emit) из документов ServiceRecord.

До сих пор я думал только о двух запросах: один, чтобы получить VIN из информации о плите, а другой - получить служебные записи от VIN.Они будут быстрыми запросами, поэтому это не огромная проблема, но я чувствую, что есть лучший способ.

Кто-нибудь знает, что может быть лучшим способом?

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

** Редактировать: Еще одна проблема, here, была предложена как возможный дубликат. Это определенно аналогичная проблема, однако предлагаемые решения не решают эту проблему. В частности, верхнее решение предполагает сохранение позиции документа в дереве. В этом случае это будет выглядеть как "index":[State, Number, Year, Month, Day]" в документе ServiceRecord. Однако мы не можем этого сделать, потому что информация о пластине может легко измениться.

+0

Возможный дубликат [Как я могу назвать другой вид в виде кушетки?] (Http://stackoverflow.com/questions/3365268/how-can-i-call-another -view-in-a-couchdb-view) –

ответ

2

Надеюсь, вы все еще вокруг. Суть ответа заключается в следующем: в CouchDb, когда вы чувствуете необходимость делать объединения, вы в 99% случаев делаете что-то неправильно. Что вам нужно сделать, так это иметь всю необходимую информацию в одном документе.

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

Что вы можете здесь сделать, это сохранить номерного знака в документе служебной записи. Не бойтесь денормализовать. Поэтому служебная запись должна выглядеть так:

{ 
    "type": "ServiceRecord", 
    "_id": "service3", 
    "carServiced": "VIN 2", 
    "carPlateNumber": "mntclmbr", 
    "date": [2016, 4, 2], 
    "item": "Alignment", 
    "cost": 150 
} 

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

{ 
    "type": "ServiceRecord", 
    "_id": "service3", 
    "carServiced": { 
     "type": "Car", 
     "_id": "VIN 2", 
     "plateNumber": "mntclmbr", 
     "plateState": "VT", 
     "make": "Jeep", 
     "model": "Wrangler", 
     "year": 2016 
     }, 
    "date": [2016, 4, 2], 
     "item": "Alignment", 
     "cost": 150 
} 

Это абсолютно нормально. Тем более, что служебная запись является моментальным снимком во времени, и вам не нужно беспокоиться об обновлении информации. Я на самом деле считаю, что это один из сценариев, где CouchDb особенно сияет, поскольку сохранение моментального снимка в основном является бесплатным обедом (в отличие от управления таблицей cars_snapshot в реляционной системе). И мы склонны забывать об этом, но очень часто (особенно в отношении продаж), нас интересуют моментальные снимки, а не современные реляционные данные (то, что было именем клиента в момент его покупки, какова была ставка налога в то время, когда он купил и т. д.). Но реляционные системы ставят нас в привычку «наиболее актуально по умолчанию», потому что управление моментальными снимками связано со значительными накладными расходами.

Суть в том, что такая денормализация в CouchDb абсолютно прекрасна. Вы находитесь в предполагаемом использовании и не будете укусаны в обратном направлении по дороге. Как CouchDb ставит: просто расслабьтесь;)

+0

Это отличный ответ. Одно из предостережений заключается в том, что для моей фактической проблемы необходимо обновить аналог «ServiceRecord» здесь * *. Например, если Jeep в моем примере получит новый номерной знак «rollovr», тогда все служебные записи для этого транспортного средства должны быть обновлены. Потому что необходимо иметь возможность находить каждую служебную запись, которую когда-либо носил автомобиль, даже если автомобиль перерегистрирован. (Вот почему я пытался отслеживать VIN, который не изменится.) –

+0

Решение, которое я реализовал, является промежуточной средой между ними. Существуют еще документы «Автомобиль» с общей информацией о группе и документы «ServiceRecord» с конкретной информацией о члене этой группы. Затем каждый «ServiceRecord» имеет поле indexKey, например «{» _id »:« VIN 2 »,« plateNumber »:« mntclmbr »,« plateState »:« VT »}', для индексирования. В редких случаях, когда автомобиль перерегистрируется, мы можем искать и обновлять все служебные записи с обновленной информацией о пластинах. Таким образом, неэффективные (но редкие) обновления в обмен на очень эффективные (и общие) поисковые запросы. –

+0

Я рад, что вы сочли это полезным! Одна мысль заключалась бы в том, что, возможно, тарелкаState может быть ее собственным документом и выглядеть так: '{id: plateState1, plateText: 'ASF256'}', а затем автомобиль будет ссылаться на таблоState как на это '{id: 'car1', plateStateId : 'plateState1'} ', чтобы вам не приходилось делать массовое обновление. Но ! Мы вернулись к решению с двумя запросами. Но это касается вопроса, который вы упомянули при запросе по состоянию, и, как вы сказали, запросы будут очень быстрыми. У вас были бы представления: 'getPlateIdByPlateText/State' и' getServicedCarByPlateId' – reddy

0

Похоже, что цепной mapreduce может обеспечить ваше решение? https://examples.cloudant.com/sales/_design/sales/index.html

+0

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

+0

Джеймс вы заметили опцию 'dbcopy', где вы копируете представление в новую базу данных?Используя эту стратегию, вы можете делать то, что хотите, даже если вам нужно создать ситуацию, когда шаг уменьшения на самом деле ничего не делает. Это может привести к «штрафу» за хранение, но не к производительности? – Raj

+0

Я пробовал несколько разных подходов с помощью 'dbcopy', но я не могу найти способ получить нужные мне данные в одном документе для создания представления. Возможно, я просто неправильно понимаю. Не могли бы вы привести пример того, как может выглядеть моя карта и функции сокращения? –

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