简体   繁体   English

在RavenDb中一对多建模以获得更好的性能

[英]Model one to many in RavenDb for better performance

I'm approaching to document database and I'm little bit confused how to map documents relationship, in a situation as follow 我正在接近文档数据库,但在以下情况下我有点困惑如何映射文档关系

public class Person
{
    public Person()
    {
    }
    public int Id { get; set; }
    public string Name { get;set;}
    public string Surname { get; set; }
    public DateTime? BirthDate { get; set; }
}


public class Car
{
    public Car() { }
    public int Id { get; set; }
    public string Name { get; set; }
    public int PersonId { get; set;}
}

A person has one or more cars for example in this way I can query the db as follow 例如,一个人以这种方式拥有一辆或多辆汽车,我可以如下查询数据库

public Car Get(int id)
    {
        Car car = null;
        using (IDocumentSession session = store.OpenSession())
        {
            car = session.Include<Car, Person>(x => x.PersonId).Load<Car>(id);
            bool isLoaded = session.Advanced.IsLoaded("people/" + car.PersonId); // true!
        }
        return car;
    }

and it's everything ok, the client makes just one request, but if I have a person and I want to show all his cars how can I query the db to do just a request? 一切正常,客户端只提出一个请求,但是如果我有一个人,并且想向他展示他的所有汽车,我如何查询数据库以仅做一个请求? I think tha I must modify the model putting a List<int> Cars in Person for reference his cars. 我认为我必须修改模型以放置List<int> Cars in Person以供参考。 Note that I don't want to embed Cars in the Person document because Cars can be referenced from others document. 请注意,我不想在“ Person文档中嵌入“ Cars ,因为可以从其他文档中引用“ Cars

Thanks. 谢谢。

You can index the Cars collection and load all the cars from the index. 您可以为Cars集合建立索引,并从索引中加载所有汽车。

The index would look like this: 索引如下所示:

public class CarIndex : AbstractIndexCreationTask<Car, CarView>
{
    public CarIndex()
    {
        Map = cars => from car in cars
                      select new
                      {
                          car.Id,
                          car.Name,
                          car.PersonId,
                      };
    }
}

The CarView class is identical to the Car class, but can be changed to better fit the indexing needs. CarView类与Car类相同,但是可以更改以更好地满足索引需求。

public class CarView
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int PersonId { get; set; }
}

You'll need to execute the index before being able to use it: 您需要先执行索引才能使用它:

new CarIndex().Execute(store);

Loading the cars for a certain person would look like this: 为某人装车看起来像这样:

using (IDocumentSession session = store.OpenSession())
{
    session.Store(new Person { Id = 1, Name = "A", Surname = "A" });
    session.Store(new Car { Id = 1, Name = "A", PersonId = 1 });
    session.Store(new Car { Id = 2, Name = "B", PersonId = 1 });
    session.Store(new Car { Id = 3, Name = "C", PersonId = 2 });
    session.SaveChanges();
}

WaitForIndexing(store); // from RavenTestBase

using (IDocumentSession session = store.OpenSession())
{
    var resultsForId1 = session
        .Query<CarView, CarIndex>()
        .ProjectFromIndexFieldsInto<CarView>()
        .Where(x => x.PersonId == 1);
    Assert.Equal(2, resultsForId1.Count());
    var resultsForId2 = session
        .Query<CarView, CarIndex>()
        .ProjectFromIndexFieldsInto<CarView>()
        .Where(x => x.PersonId == 2);
    Assert.Equal(1, resultsForId2.Count());
}

If you want to load the person and their cars in a single database request, use lazy loading: 如果要在单个数据库请求中加载人员及其汽车,请使用延迟加载:

var resultsForId1 = session
    .Query<CarView, CarIndex>()
    .ProjectFromIndexFieldsInto<CarView>()
    .Where(x => x.PersonId == 1).Lazily();
var person = session.Advanced.Lazily.Load<Person>(1);

var personValue = person.Value;
var resultsValue = resultsForId1.Value;

Complete test (needs xunit and RavenDB.Tests.Helpers nugets): 完整测试(需要xunit和RavenDB.Tests.Helpers nugets):

using Raven.Client;
using Raven.Client.Indexes;
using Raven.Tests.Helpers;
using System;
using System.Linq;
using Xunit;

namespace SO41547501Answer
{
    public class SO41547501 : RavenTestBase
    {
        [Fact]
        public void SO41547501Test()
        {
            using (var server = GetNewServer())
            using (var store = NewRemoteDocumentStore(ravenDbServer: server))
            {
                new CarIndex().Execute(store);

                using (IDocumentSession session = store.OpenSession())
                {
                    session.Store(new Person { Id = 1, Name = "A", Surname = "A" });
                    session.Store(new Car { Id = 1, Name = "A", PersonId = 1 });
                    session.Store(new Car { Id = 2, Name = "B", PersonId = 1 });
                    session.Store(new Car { Id = 3, Name = "C", PersonId = 2 });
                    session.SaveChanges();
                }

                WaitForAllRequestsToComplete(server);
                WaitForIndexing(store);

                using (IDocumentSession session = store.OpenSession())
                {
                    var resultsForId1 = session
                        .Query<CarView, CarIndex>()
                        .ProjectFromIndexFieldsInto<CarView>()
                        .Where(x => x.PersonId == 1);
                    Assert.Equal(2, resultsForId1.Count());
                    var resultsForId2 = session
                        .Query<CarView, CarIndex>()
                        .ProjectFromIndexFieldsInto<CarView>()
                        .Where(x => x.PersonId == 2);
                    Assert.Equal(1, resultsForId2.Count());
                }

                using (IDocumentSession session = store.OpenSession())
                {
                    server.Server.ResetNumberOfRequests();
                    var resultsForId1 = session
                        .Query<CarView, CarIndex>()
                        .ProjectFromIndexFieldsInto<CarView>()
                        .Where(x => x.PersonId == 1).Lazily();
                    var person = session.Advanced.Lazily.Load<Person>(1);

                    var personValue = person.Value;
                    var resultsValue = resultsForId1.Value;
                    Assert.Equal("A", personValue.Name); // person data loaded
                    Assert.Equal("A", resultsValue.First().Name); // cars data loaded
                    Assert.Equal(1, server.Server.NumberOfRequests); // only one request sent to the server
                }
            }
        }
    }

    public class CarIndex : AbstractIndexCreationTask<Car, CarView>
    {
        public CarIndex()
        {
            Map = cars => from car in cars
                          select new
                          {
                              car.Id,
                              car.Name,
                              car.PersonId,
                          };
        }
    }

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
        public DateTime? BirthDate { get; set; }
    }

    public class Car
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int PersonId { get; set; }
    }

    public class CarView
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int PersonId { get; set; }
    }
}

You can do it like this: 您可以这样做:

using (IDocumentSession session = store.OpenSession())
{
    var carsForOne = session.Query<Car>()
             .Include(x=>x.PersonId)
             .Where(x=>x.PersonId == "people/1")
             .ToList();

    var person = session.Load<Person>("people/1");
}

This make just a single db request. 这仅发出一个数据库请求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM