2017-01-12 9 views
0

이 질문은 RavenDB: Why do I get null-values for fields in this multi-map/reduce index?의 스핀 오프이지만, 문제는 또 다른 것이 었습니다.RavenDB : map-reduce에서 카티 젼 제품을 올바르게 색인화하려면 어떻게해야합니까?

public class User 
{ 
    public string Id { get; set; } 
} 

public class Movie 
{ 
    public string Id { get; set; } 
} 

public class MovieRental 
{ 
    public string Id { get; set; } 
    public string MovieId { get; set; } 
    public string UserId { get; set; } 
} 

그건 텍스트 책 대다 예 :

추상화에 대한 영화 대여점 시나리오에 다시 내 매우 단순화 된 도메인을 고려하십시오. 방법을 설명 정수와 함께 (필터링/검색 순간 왼쪽으로) 지정된 사용자에 대해

, 데이터베이스 나에게 모든 영화의 목록을 제공 :

내가 만들 지수는 이것이다 사용자가이 영화를 대여 한 횟수 (또는 0). 기본적으로이 같은

:

사용자 :

| Id  | 
|--------| 
| John | 
| Lizzie | 
| Albert | 

동영상 :

| Id   | 
|--------------| 
| Robocop  | 
| Notting Hill | 
| Inception | 

MovieRentals :

선언적

| UserId | MovieId  | RentalCount | 
|--------|--------------|-------------| 
| John | Robocop  | 1   | 
| John | Notting Hill | 2   | 
| John | Inception | 0   | 
| Lizzie | Robocop  | 2   | 
| Lizzie | Notting Hill | 0   | 
| Lizzie | Inception | 1   | 
| Albert | Robocop  | 0   | 
| Albert | Notting Hill | 0   | 
| Albert | Inception | 0   | 

또는 : 이상적으로

| Id  | UserId | MovieId  | 
|-----------|--------|--------------| 
| rental-00 | John | Robocop  | 
| rental-01 | John | Notting Hill | 
| rental-02 | John | Notting Hill | 
| rental-03 | Lizzie | Robocop  | 
| rental-04 | Lizzie | Robocop  | 
| rental-05 | Lizzie | Inception | 

, I는 다음과 같이 것이라고, 인덱스 조회 할 난 항상 결국 (모든 영화의 전체 목록을 원하는

  • 를 I 필터링/검색 기능 추가) - 단 하나의 영화를 대여 한 적이없는 사용자에게 제공하는 경우에도
  • 각 사용자의 대여 횟수를 알고 싶습니다. 정수는
  • 입니다. 내가 할 수있는 방법을 찾을 수없는, 즉

그러나 목록의 상단에 해당 사용자에 대한 가장 대여 한 동영상을 보여 -

  • 나는 임대 카운트를 기준으로 정렬 할 수 있도록하려면 위의 "교차 조인"을 선택하고 색인에 저장하십시오. 대신에, 나는 처음에 내가 바로 아래이 기동로를 가지고 생각하지만, (실패 테스트 참조) 나 정렬을 허용하지 않습니다

    { "지원되지 않음 계산을 : x.UserRentalCounts.SingleOrDefault을 (rentalCount => (rentalCount.UserId == 값 (UnitTestProject2.MovieRentalTests + <> c__DisplayClass0_0) .user_john.Id)).. 당신은 RavenDB 쿼리 (단순한 회원 표현을 사용할 수 있습니다). "}

    내 질문은에 계산을 사용할 수 없습니다 카운트 기본적으로 : 나는 어떻게 할 수 있습니까? 아니면 전혀 할 수 있습니까?


    다음은 내 요구 사항을 충족하지 않는 내 언급 한 예입니다,하지만 난 지금의 곳이다.그것은 다음과 같은 패키지 (VS2015) 사용

    packages.config

    <?xml version="1.0" encoding="utf-8"?> 
    <packages> 
        <package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net461" /> 
        <package id="NUnit" version="3.5.0" targetFramework="net461" /> 
        <package id="RavenDB.Client" version="3.5.2" targetFramework="net461" /> 
        <package id="RavenDB.Database" version="3.5.2" targetFramework="net461" /> 
        <package id="RavenDB.Tests.Helpers" version="3.5.2" targetFramework="net461" /> 
    </packages> 
    

    MovieRentalTests.cs을 귀하의 요구 사항은 "특정 사용자에 대한"라고 때문에

    using System.Collections.Generic; 
    using System.Linq; 
    using NUnit.Framework; 
    using Raven.Client.Indexes; 
    using Raven.Client.Linq; 
    using Raven.Tests.Helpers; 
    
    namespace UnitTestProject2 
    { 
        [TestFixture] 
        public class MovieRentalTests : RavenTestBase 
        { 
         [Test] 
         public void DoSomeTests() 
         { 
          using (var server = GetNewServer()) 
          using (var store = NewRemoteDocumentStore(ravenDbServer: server)) 
          { 
           //Test-data 
           var user_john = new User { Id = "John" }; 
           var user_lizzie = new User { Id = "Lizzie" }; 
           var user_albert = new User { Id = "Albert" }; 
    
    
           var movie_robocop = new Movie { Id = "Robocop" }; 
           var movie_nottingHill = new Movie { Id = "Notting Hill" }; 
           var movie_inception = new Movie { Id = "Inception" }; 
    
           var rentals = new List<MovieRental> 
           { 
            new MovieRental {Id = "rental-00", UserId = user_john.Id, MovieId = movie_robocop.Id}, 
            new MovieRental {Id = "rental-01", UserId = user_john.Id, MovieId = movie_nottingHill.Id}, 
            new MovieRental {Id = "rental-02", UserId = user_john.Id, MovieId = movie_nottingHill.Id}, 
            new MovieRental {Id = "rental-03", UserId = user_lizzie.Id, MovieId = movie_robocop.Id}, 
            new MovieRental {Id = "rental-04", UserId = user_lizzie.Id, MovieId = movie_robocop.Id}, 
            new MovieRental {Id = "rental-05", UserId = user_lizzie.Id, MovieId = movie_inception.Id} 
           }; 
    
           //Init index 
           new Movies_WithRentalsByUsersCount().Execute(store); 
    
           //Insert test-data in db 
           using (var session = store.OpenSession()) 
           { 
            session.Store(user_john); 
            session.Store(user_lizzie); 
            session.Store(user_albert); 
    
            session.Store(movie_robocop); 
            session.Store(movie_nottingHill); 
            session.Store(movie_inception); 
    
            foreach (var rental in rentals) 
            { 
             session.Store(rental); 
            } 
    
            session.SaveChanges(); 
    
            WaitForAllRequestsToComplete(server); 
            WaitForIndexing(store); 
           } 
    
           //Test of correct rental-counts for users 
           using (var session = store.OpenSession()) 
           { 
            var allMoviesWithRentalCounts = 
             session.Query<Movies_WithRentalsByUsersCount.ReducedResult, Movies_WithRentalsByUsersCount>() 
              .ToList(); 
    
            var robocopWithRentalsCounts = allMoviesWithRentalCounts.Single(m => m.MovieId == movie_robocop.Id); 
            Assert.AreEqual(1, robocopWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_john.Id)?.Count ?? 0); 
            Assert.AreEqual(2, robocopWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_lizzie.Id)?.Count ?? 0); 
            Assert.AreEqual(0, robocopWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_albert.Id)?.Count ?? 0); 
    
            var nottingHillWithRentalsCounts = allMoviesWithRentalCounts.Single(m => m.MovieId == movie_nottingHill.Id); 
            Assert.AreEqual(2, nottingHillWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_john.Id)?.Count ?? 0); 
            Assert.AreEqual(0, nottingHillWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_lizzie.Id)?.Count ?? 0); 
            Assert.AreEqual(0, nottingHillWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_albert.Id)?.Count ?? 0); 
           } 
    
           // Test that you for a given user can sort the movies by view-count 
           using (var session = store.OpenSession()) 
           { 
            var allMoviesWithRentalCounts = 
             session.Query<Movies_WithRentalsByUsersCount.ReducedResult, Movies_WithRentalsByUsersCount>() 
              .OrderByDescending(x => x.UserRentalCounts.SingleOrDefault(rentalCount => rentalCount.UserId == user_john.Id).Count) 
              .ToList(); 
    
            Assert.AreEqual(movie_nottingHill.Id, allMoviesWithRentalCounts[0].MovieId); 
            Assert.AreEqual(movie_robocop.Id, allMoviesWithRentalCounts[1].MovieId); 
            Assert.AreEqual(movie_inception.Id, allMoviesWithRentalCounts[2].MovieId); 
           } 
          } 
         } 
    
         public class Movies_WithRentalsByUsersCount : 
          AbstractMultiMapIndexCreationTask<Movies_WithRentalsByUsersCount.ReducedResult> 
         { 
          public Movies_WithRentalsByUsersCount() 
          { 
           AddMap<MovieRental>(rentals => 
            from r in rentals 
            select new ReducedResult 
            { 
             MovieId = r.MovieId, 
             UserRentalCounts = new[] { new UserRentalCount { UserId = r.UserId, Count = 1 } } 
            }); 
    
           AddMap<Movie>(movies => 
            from m in movies 
            select new ReducedResult 
            { 
             MovieId = m.Id, 
             UserRentalCounts = new[] { new UserRentalCount { UserId = null, Count = 0 } } 
            }); 
    
           Reduce = results => 
            from result in results 
            group result by result.MovieId 
            into g 
            select new 
            { 
             MovieId = g.Key, 
             UserRentalCounts = (
               from userRentalCount in g.SelectMany(x => x.UserRentalCounts) 
               group userRentalCount by userRentalCount.UserId 
               into subGroup 
               select new UserRentalCount { UserId = subGroup.Key, Count = subGroup.Sum(b => b.Count) }) 
              .ToArray() 
            }; 
          } 
    
          public class ReducedResult 
          { 
           public string MovieId { get; set; } 
           public UserRentalCount[] UserRentalCounts { get; set; } 
          } 
    
          public class UserRentalCount 
          { 
           public string UserId { get; set; } 
           public int Count { get; set; } 
          } 
         } 
    
         public class User 
         { 
          public string Id { get; set; } 
         } 
    
         public class Movie 
         { 
          public string Id { get; set; } 
         } 
    
         public class MovieRental 
         { 
          public string Id { get; set; } 
          public string MovieId { get; set; } 
          public string UserId { get; set; } 
         } 
        } 
    } 
    
  • 답변

    1

    , 당신이 경우 정말 단일 사용자 만 찾고 있다면 멀티 맵 색인으로이 작업을 수행 할 수 있습니다. 무비 테이블 자체를 사용하여 기준 제로 카운트 레코드를 생성 한 다음 그 위에 사용자의 실제 MovieRentals 레코드를 매핑하십시오.

    모든 사용자가 모든 영화를 볼 때 정말 필요한 경우라면 RavenDB와 완전히 동일하게 처리하는 방법이 있다고 생각하지 않습니다. reporting which is noted as one of the sour spots for RavenDB으로 간주됩니다.

    1) 모든 사용자와 모든 영화 DB에 더미 기록을 작성하고 0 카운트 색인 사람들을 사용 : 당신이 정말 RavenDB이 작업을 수행하려고 시도하는 경우 여기

    은 몇 가지 옵션이 있습니다. 영화 또는 사용자가 추가/업데이트/삭제 될 때마다 더미 레코드를 적절하게 업데이트하십시오.

    2) 요청시 메모리에 0 카운트 레코드를 생성하고 RavenDB가 0이 아닌 카운트를 반환하는 데이터와 해당 데이터를 병합합니다. 모든 사용자를 쿼리하고 모든 영화를 쿼리하며 기본 0 카운트 레코드를 만든 다음 0이 아닌 카운트에 대한 실제 쿼리를 수행하고 맨 위에 레이어를 만듭니다. 마지막으로 페이징/필터링/정렬 논리를 적용합니다.

    3) SQL 복제 번들을 사용하여 Users, Movies 및 MovieRental 테이블을 SQL에 복제하고이 "보고"쿼리에 SQL을 사용하십시오.

    +0

    감사합니다. David. 옵션 2를 좋아하지 않습니다. 왜냐하면 카운트별로 정렬해야 할 때 페이징을 고려할 때 메모리에 "모든 것"을로드해야하기 때문입니다. 옵션 3은 분명히 갈 길이며, RavenDB를 선택할 때 집합과 같은 제한이 있습니다. 이 특정 상황에서 응답을 기다리는 동안 "좋아하는 영화"목록을 "모든 영화"목록과 분리하여 보관했습니다 (따라서 그룹화 및 합계를 사용하여 'MovieRentals'의 간단한 색인) - 더 나은 것으로 나타났습니다. UX를 ​​사용합니다. 다시 한 번 감사 드리며 여기에 당신의 현상금이 있습니다 :) –