2013-02-18 31 views
7

Đồng nghiệp của tôi đã hỏi tôi nếu có ví dụ orderline, nó muốn được có thể để khởi tạo một viewmodel tìm kiếm như thế này:Transforming Kết quả

OrderViewModel 
    string OrderId 
    string CustomerName 
    List<OrderLineViewModel> OrderLines 

OrderLineViewModel 
    string ProductName 
    string ROI 
    int Quantity 

Từ một chỉ số?

Tôi đã thử thực hiện chuyển đổi tải thành công tên khách hàng nhưng không bao giờ có thể quản lý để nhận thông tin sản phẩm được liên kết từ đơn đặt hàng. Điều này có thể được thực hiện với một biến đổi hoặc tôi sẽ cần phải dự án từ các lĩnh vực chỉ số?

Chúc mừng,

James

EDIT:

Chúng tôi đang cố gắng để cư xem các mô hình trực tiếp từ một truy vấn. Chúng tôi đã thử các chỉ số sau:

public class OrdersViewIndex : AbstractIndexCreationTask<Order> 
{ 
    Map = orders => from order in orders 
        select new { 
           OrderId = order.id 
           }; 

    Transform = (database, orders) => from order in orders 
            let customer = database.Load<Customer>(order.customerId) 
            select new { 
                OrderId = order.id, 
                CustomerName = customer.Name, 
                OrderLines = // This is where I struggled to answer my colleagues questions as i'd need to load product name. 
               } 
} 
+0

Trong hầu hết các trường hợp, bạn không cần chuyển đổi và chỉ có thể thực hiện việc này trong mã của riêng bạn. Vui lòng đăng những gì bạn đã thử. –

Trả lời

18

Đầu tiên, nhận ra rằng mọi chỉ số tự động lập bản đồ Id vào một mục từ được gọi là __document_id. Vì vậy, không có nhiều giá trị trong việc lập bản đồ lại. Tất cả những gì bạn đang làm trong bản đồ chỉ mục này là sao chép lại nó vào một mục nhập chỉ mục khác có tên là OrderId.

Thứ hai, hiểu rằng các biến đổi không thực sự là một phần của chỉ mục, nhưng chỉ gắn liền với định nghĩa chỉ mục và được thực thi khi chạy. Tất cả những gì họ thực sự cung cấp là một cách để biến đổi kết quả truy vấn trên máy chủ. Trong hầu hết các trường hợp, đây là những thứ bạn có thể làm ở phía máy khách.

Thứ ba, chỉ mục dành cho truy vấn đối với các trường không phải id và cung cấp kết quả possibly stale nhưng eventually consistent. Khi bạn truy xuất tài liệu theo số Id (còn được gọi là khóa tài liệu ), thì hoàn toàn không có điểm nào khi sử dụng chỉ mục. Thay vào đó, bạn muốn sử dụng phương thức .Load(), cung cấp bảo đảm ACID và chỉ truy xuất tài liệu từ cơ sở dữ liệu.

Bây giờ - bạn có câu hỏi về cách lấy tên khách hàng khi tài liệu của bạn chỉ có id khách hàng và cách lấy tên sản phẩm thay vì chỉ là id sản phẩm. Giả sử các tài liệu của bạn trông như thế này:

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; } 
} 

Nếu bạn đang lấy một trật tự đơn sử dụng id của nó, bạn sẽ làm gì sau đây:

var order = session.Load<Order>(theOrderId); 

Nhưng bây giờ bạn muốn điền một số mô hình điểm như thế này :

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; } 
} 

Bạn sẽ làm điều này bằng cách sử dụng 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 
       }) 
}; 

Mặc dù nhìn thấy nhiều cuộc gọi đến session.Load(), chỉ thực sự có một cuộc gọi đến cơ sở dữ liệu. Tuyên bố .Include đảm bảo tất cả các tài liệu liên quan được tải vào phiên với cuộc gọi đầu tiên. Các cuộc gọi tiếp theo chỉ cần kéo nó ra khỏi phiên địa phương.

Tất cả những điều trên là để truy lục một đơn đặt hàng theo id của nó.Thay vào đó, nếu bạn muốn nhận tất cả đơn đặt hàng hoặc nhận tất cả các đơn đặt hàng cho một khách hàng cụ thể - thì bạn cần truy vấn.

Một truy vấn năng động cho các đơn hàng của một khách hàng cụ thể sẽ trông như thế này:

var results = session.Query<Order>().Where(x => x.CustomerId == theCustomerId); 

Nếu bạn muốn dự án này để xem các mô hình của bạn, giống như trước khi bạn có thể sử dụng bao gồm:

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 
     }) 
    }); 

Điều này làm việc, nhưng bạn có thể không muốn viết này mỗi lần. Ngoài ra, toàn bộ sản phẩm và hồ sơ khách hàng phải được tải trong phiên, khi bạn chỉ muốn một trường duy nhất từ ​​mỗi phiên. Đây là nơi biến đổi có thể hữu ích. Bạn có thể xác định chỉ mục tĩnh như sau:

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 
        }) 
      }; 
    } 
} 

Bây giờ khi bạn truy vấn, biến đổi đã thiết lập dữ liệu cho bạn. Bạn chỉ cần xác định VM để tạo dự án.

var results = session.Query<Order, Orders_Transformed>().As<OrderVM>(); 

Bạn có thể nhận thấy rằng tôi không bao gồm bất kỳ trường nào trong bản đồ chỉ mục. Đó là bởi vì tôi đã không cố gắng truy vấn trên bất kỳ lĩnh vực cụ thể. Tất cả dữ liệu đến từ chính tài liệu - các mục duy nhất trong chỉ mục được tự động thêm __document_id mục và Raven sử dụng dữ liệu đó để trình bày dữ liệu từ kho lưu trữ tài liệu - để trả lại hoặc để chuyển đổi.

Bây giờ hãy nói rằng tôi muốn truy vấn theo một trong các trường liên quan đó. Ví dụ: tôi muốn nhận tất cả đơn đặt hàng cho khách hàng có tên là Joe. Để thực hiện điều này, tôi cần phải bao gồm tên khách hàng trong chỉ mục của mình. RavenDB 2.0 đã thêm một tính năng giúp việc này trở nên dễ dàng - Indexing Related Documents.

Bạn sẽ cần phải sửa đổi bản đồ chỉ số để sử dụng phương pháp LoadDocument, như sau:

Map = orders => from order in orders 
       select new 
       { 
        CustomerName = LoadDocument<Customer>(order.CustomerId) 
       }; 

Nếu bạn thích, bạn có thể kết hợp này với một trong hai Bao gồm, hoặc Transform kỹ thuật để lấy lại đầy đủ xem mô hình.

Một kỹ thuật khác là lưu trữ các trường này và project from the index. Điều này hoạt động rất tốt cho các trường đơn lẻ như CustomerName, nhưng có lẽ quá mức cần thiết cho các giá trị phức tạp như OrderLines.

Và cuối cùng, một kỹ thuật khác cần xem xét là denormalization. Hãy xem xét một chút rằng Product có thể đã thay đổi tên hoặc bị xóa. Có thể bạn không muốn vô hiệu hóa các đơn đặt hàng trước đó. Sẽ là một ý tưởng hay khi sao chép bất kỳ dữ liệu sản phẩm nào có liên quan đến đơn đặt hàng vào đối tượng OrderLine.

public class OrderLine 
{ 
    public string ProductId { get; set; } 
    public string ProductName { get; set; } 
    public decimal Price { get; set; } 
    public int Quantity { get; set; } 
} 

Khi bạn làm điều đó - bạn không còn cần phải tải dữ liệu sản phẩm khi truy xuất đơn đặt hàng nữa. Phần chuyển đổi trở nên không cần thiết, và bạn là trái với một dự báo chỉ số đơn giản, như sau:

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); 
    } 
} 

Mà bạn có thể truy vấn một cách dễ dàng với:

var results = session.Query<OrderVM, Orders_ByCustomerName>() 
        .Where(x => x.CustomerName == "Joe") 
        .As<OrderVM>(); 

Lưu ý trong truy vấn, lần đầu tiên tôi chỉ định OrderVM, tôi xác định hình dạng của các mục nhập chỉ mục. Nó chỉ thiết lập lambdas để tôi có thể chỉ định x.CustomerName == "Joe".Thông thường, bạn sẽ thấy một lớp "Kết quả" đặc biệt được sử dụng cho mục đích này. Nó thực sự không quan trọng - tôi có thể sử dụng bất kỳ lớp nào có trường chuỗi CustomerName.

Khi tôi định .As<OrderVM>() - đó là nơi tôi thực sự di chuyển từ một loại Order đến một loại OrderVM - và lĩnh vực CustomerName đến cùng cho đi xe từ khi chúng tôi bật lưu trữ lĩnh vực cho nó.

TL; DR

RavenDB có nhiều lựa chọn. Thử nghiệm để tìm những gì phù hợp với nhu cầu của bạn. Thiết kế tài liệu phù hợp và sử dụng cẩn thận Indexing Related Documents với LoadDocument() sẽ luôn loại bỏ nhu cầu chuyển đổi chỉ mục.

+0

Cảm ơn bạn rất nhiều vì đã dành thời gian cho Matt, vì mọi câu trả lời của bạn đều rất hữu ích .. Cái này đứng đầu tất cả! - Bản thân tôi và những người làm việc đã phải vật lộn để tìm ra/không của ai và khi nào/whys - Tôi sẽ được mọi người đọc câu trả lời này! - cảm ơn lần nữa! – Jamez

+0

Trong RavenDB 2.5, bây giờ có khái niệm về [Kết quả Transformers] (http://ravendb.net/docs/2.5/client-api/querying/results-transformation/result-transformers). Tôi sẽ sớm cập nhật câu trả lời này để giải thích cho họ. –

Các vấn đề liên quan