首先,實現各項指標的自動Id
映射到名爲__document_id
索引條目。所以再次映射它沒有太大的價值。您在這張索引圖中所做的所有操作都是將其複製到另一個索引條目OrderId
。其次,瞭解轉換不是索引的一部分,但只是附加到索引定義並在運行時執行。他們真正提供的是一種在服務器上改變查詢結果的方法。在大多數情況下,這些都是客戶端可以做的事情。
第三,索引用於查詢非id字段,並提供possibly stale但是eventually consistent結果。當您通過Id
(也稱爲文檔鍵)檢索文檔時,根本沒有必要使用索引。您希望使用.Load()
方法,該方法提供了ACID保證,並且只是從數據庫中檢索文檔。
現在 - 當您的文檔具有客戶ID時,您有如何獲取客戶名稱的問題,以及如何獲取產品名稱而不僅僅是產品ID。讓我們假設你的文件是這樣的:
public class Order
{
public string Id { get; set; }
public string CustomerId { get; set; }
public List<OrderLine> OrderLines { get; set; }
}
public class OrderLine
{
public string ProductId { get; set; }
public int Quantity { get; set; }
}
public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
}
如果您正在使用其ID檢索一個訂單,你會做以下幾點:
var order = session.Load<Order>(theOrderId);
但現在要填充一些視圖模型這樣的:
public class OrderVM
{
public string OrderId { get; set; }
public string CustomerId { get; set; }
public string CustomerName { get; set; }
public List<OrderLineVM> OrderLines { get; set; }
}
public class OrderLineVM
{
public string ProductId { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
}
你會這樣做,使用Includes。
var order = session.Include<Order>(x => x.CustomerId)
.Include<Order>(x => x.OrderLines.Select(y => y.ProductId))
.Load<Order>(theOrderId);
var orderViewModel = new OrderVM
{
OrderId = order.Id,
CustomerId = order.CustomerId,
CustomerName = session.Load<Customer>(order.CustomerId).Name,
OrderLines = order.OrderLines.Select(x => new OrderLineVM
{
ProductId = x.ProductId,
ProductName = session.Load<Product>(x.ProductId).Name,
Quantity = x.Quantity
})
};
儘管看到多個呼叫到session.Load()
,實際上只有一個調用數據庫。 .Include
聲明確保所有相關文檔都在首次調用時加載到會話中。隨後的調用將其從本地會話中拉出。
以上所有內容都是通過它的id檢索單個訂單。如果您想要獲得所有訂單,或者獲取特定客戶的所有訂單 - ,然後您需要查詢。
特定客戶的訂單動態查詢應該是這樣的:
var results = session.Query<Order>().Where(x => x.CustomerId == theCustomerId);
如果你想項目中的這些對你的視圖模型,就像你可以使用之前包括:
var results = session.Query<Order>()
.Customize(x => x.Include<Order>(y => y.CustomerId)
.Include<Order>(y => y.OrderLines.Select(z => z.ProductId)))
.Where(x => x.CustomerId == theCustomerId)
.Select(x => new OrderVM
{
OrderId = x.Id,
CustomerId = x.CustomerId,
CustomerName = session.Load<Customer>(x.CustomerId).Name,
OrderLines = order.OrderLines.Select(y => new OrderLineVM
{
ProductId = y.ProductId,
ProductName = session.Load<Product>(y.ProductId).Name,
Quantity = y.Quantity
})
});
這確實有用,但你可能不想每次都寫這個。另外,當你只需要一個字段時,整個產品和客戶記錄都必須在會話中加載。這是轉換可能有用的地方。您可以按如下方式定義靜態索引:
public class Orders_Transformed : AbstractIndexCreationTask<Order>
{
public Orders_Transformed()
{
Map = orders => from order in orders select new { };
TransformResults = (database, orders) =>
from order in orders
select new
{
OrderID = order.Id,
CustomerID = order.CustomerId,
CustomerName = database.Load<Customer>(order.CustomerId).Name,
OrderLines = order.OrderLines.Select(y => new
{
ProductId = y.ProductId,
ProductName = database.Load<Product>(y.ProductId).Name,
Quantity = y.Quantity
})
};
}
}
現在,當您查詢時,轉換已經爲您設置了數據。您只需指定生成的VM即可投入。
var results = session.Query<Order, Orders_Transformed>().As<OrderVM>();
您可能已經注意到我沒有在索引圖中包含任何字段。那是因爲我沒有試圖查詢任何特定的字段。所有數據都來自文檔本身 - 索引中的唯一條目是自動添加的__document_id
條目,而Raven使用這些條目來呈現文檔存儲中的數據 - 用於返回或轉換。
現在讓我們假設我想通過其中一個相關字段進行查詢。例如,我想獲得名爲Joe的所有訂單。要做到這一點,我需要在我的索引中包含客戶名稱。 RavenDB 2.0增加了一項功能,使其非常簡單 - Indexing Related Documents。
您需要修改索引地圖使用LoadDocument
方法,如下所示:
Map = orders => from order in orders
select new
{
CustomerName = LoadDocument<Customer>(order.CustomerId)
};
如果你喜歡,你可以用任何的包括結合這一點,或者變換技術,以全額取回查看模型。
另一種技術是存儲這些字段和project from the index。這對於CustomerName
這樣的單個字段非常有效,但對於像OrderLines
這樣的複雜值可能是過度的。
最後,另一種考慮的技術是denormalization。暫時考慮一下Product
可能會更改其名稱,或者被刪除。您可能不希望使先前的訂單失效。將與訂單相關的任何產品數據複製到OrderLine
對象中將是一個好主意。
public class OrderLine
{
public string ProductId { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
一旦你這樣做 - 當檢索訂單時,不再需要加載產品數據。變換部件變得不必要,並且如果只剩下一個簡單的指標投影,如下所示:
public class Orders_ByCustomerName : AbstractIndexCreationTask<Order>
{
public Orders_ByCustomerName()
{
Map = orders => from order in orders
select new
{
CustomerName = LoadDocument<Customer>(order.CustomerId).Name
};
Store("CustomerName", FieldStorage.Yes);
}
}
,您可以輕鬆地查詢:
var results = session.Query<OrderVM, Orders_ByCustomerName>()
.Where(x => x.CustomerName == "Joe")
.As<OrderVM>();
注意查詢,我第一次指定OrderVM
,我正在定義索引條目的形狀。它只是設置lambda表達式,所以我可以指定x.CustomerName == "Joe"
。通常,您會看到一個特殊的「結果」類用於此目的。這真的沒關係 - 我可以使用任何具有CustomerName
字符串字段的類。
當我指定.As<OrderVM>()
- 那是我真正從Order
型移動到OrderVM
型 - 和CustomerName
場走來的行列,因爲我們打開字段存儲它。
TL; DR
RavenDB有很多的選擇。試着找出適合你需求的東西。正確的文檔設計和小心使用Indexing Related Documents和LoadDocument()
大多數情況下都會消除對索引轉換的需求。
在大多數情況下,您不需要轉換,只需在自己的代碼中執行此操作即可。請發佈你已經嘗試過的。 – 2013-02-18 18:41:44