Cosmos Design Вопросы - PullRequest
       13

Cosmos Design Вопросы

0 голосов
/ 06 мая 2018

У меня есть следующий упрощенный дизайн верхнего уровня для моего проекта Cosmos. Некоторое введение, а затем несколько вопросов. Заранее благодарим за прочтение этого.

У меня есть 3 коллекции: Memberships, Discounts и Vouchers:

enter image description here

В каждой коллекции хранятся разные сущности. Объекты в каждой коллекции не являются одинаковыми, но принадлежат одному разделу и имеют унаследованные свойства:

  • id - уникальный идентификатор (требование Космоса ... я думаю)
  • upsertDate - обновляется во время upsert, поэтому я могу заказать документы (см. Ниже)

Каждая сущность коллекции имеет:

  • code - ключ раздела
  • type - дискриминатор для различных сущностей в коллекции.

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

Если это сделать, он возвращает все документы сущности, принадлежащие одному и тому же code:

SELECT * FROM c WHERE c.code = 'YTR3IO'

Если это сделать, он возвращает все документы сущностей, которые принадлежат тем же code и type:

SELECT * FROM c WHERE c.code = 'YTR3IO' and c.type = 10

type обычно является перечислением для различных сущностей, которые могут храниться в определенной коллекции. Например, в случае членства тип может быть notication, comment, request, transaction и т. Д.

Причина, по которой у меня есть upsertDate в каждом документе, чтобы я мог делать такие вещи:

SELECT * FROM c WHERE c.code = 'YTR3IO' AND c.type = 10 ORDER BY c.upsertDate DESC

Для политики индексирования я переопределил политику на то, что мне кажется лучшим:

DocumentCollection collectionDefinition = new DocumentCollection();
collectionDefinition.Id = _docDbMembershipsCollectionName;
collectionDefinition.PartitionKey.Paths.Add("/code");
collectionDefinition.DefaultTimeToLive = -1;

// If queries are known upfront, index just the properties we need
collectionDefinition.IndexingPolicy.Automatic = true;
collectionDefinition.IndexingPolicy.IndexingMode = IndexingMode.Consistent;
collectionDefinition.IndexingPolicy.IncludedPaths.Clear();

IncludedPath path = new IncludedPath();
path.Path = "/code/?";
path.Indexes.Add(new RangeIndex(DataType.String) { Precision = -1 });
collectionDefinition.IndexingPolicy.IncludedPaths.Add(path);

path = new IncludedPath();
path.Path = "/upsertDate/?";
path.Indexes.Add(new RangeIndex(DataType.String) { Precision = -1 });
collectionDefinition.IndexingPolicy.IncludedPaths.Add(path);

path = new IncludedPath();
path.Path = "/type/?";
path.Indexes.Add(new RangeIndex(DataType.Number) { Precision = -1 });
collectionDefinition.IndexingPolicy.IncludedPaths.Add(path);

collectionDefinition.IndexingPolicy.ExcludedPaths.Clear();
collectionDefinition.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/*" });

DocumentCollection ttlEnabledCollection = await 
_docDbClient.CreateDocumentCollectionAsync(
UriFactory.CreateDatabaseUri(_docDbDatabaseName), collectionDefinition, new RequestOptions { OfferThroughput = 1000 });

ВОПРОСЫ:

ВОПРОС 1: Если у меня есть запрос, у которого есть переадресация по upsertDate, стоимость поднимается вверх:

FeedOptions queryOptions = new FeedOptions { MaxItemCount = 100, PartitionKey = new Microsoft.Azure.Documents.PartitionKey(code.ToUpper()) };

var query =  this._docDbClient.CreateDocumentQuery<MembershipDeviceRegistrationItem>(
                UriFactory.CreateDocumentCollectionUri(_docDbDatabaseName, _docDbMembershipsCollectionName), queryOptions)
                .Where(e => e.CollectionType == MembershipCollectionTypes.DeviceRegistrationItem && e.Code.ToUpper() == code.ToUpper())
                .OrderByDescending(e => e.UpsertDate)
                .AsDocumentQuery();

List<MembershipDeviceRegistrationItem> allDocuments = new List<MembershipDeviceRegistrationItem>();
while (query.HasMoreResults)
{
    var queryResult = await query.ExecuteNextAsync<MembershipDeviceRegistrationItem>();
    cost += queryResult.RequestCharge;
    allDocuments.AddRange(queryResult.ToList());
}

return allDocuments.ToList();

Как я могу использовать upsertDate, чтобы заказать мои документы и выбрать, например, самые последние из них?

ВОПРОС 2: Как рассчитать стоимость запроса с агрегатом?

FeedOptions queryOptions = new FeedOptions { MaxItemCount = 1, PartitionKey = new Microsoft.Azure.Documents.PartitionKey(code.ToUpper()) };

return _docDbClient.CreateDocumentQuery<int>(
            UriFactory.CreateDocumentCollectionUri(_docDbDatabaseName, _docDbMembershipsCollectionName),
            new SqlQuerySpec()
            {
                QueryText = $"SELECT value count(m.id) FROM {_docDbMembershipsCollectionName} m WHERE m.code = @code AND m.collectionType = @type",
                Parameters = new SqlParameterCollection()
                {
                    new SqlParameter("@code", code.ToUpper()),
                    new SqlParameter("@type", MembershipCollectionTypes.DeviceRegistrationItem)
                }
            }, queryOptions).AsEnumerable().First();

ВОПРОС 3: Мне нужно создать документ Omni, который содержит все о конкретном членстве, и вернуться к какому-либо клиенту, чтобы они могли показать его различные части. Поэтому я создал что-то вроде этого (упрощенно):

FeedOptions queryOptions = new FeedOptions { MaxItemCount = -1, PartitionKey = new Microsoft.Azure.Documents.PartitionKey(code.ToUpper()) };
var query = _docDbClient.CreateDocumentQuery<dynamic>(
            UriFactory.CreateDocumentCollectionUri(_docDbDatabaseName, _docDbMembershipsCollectionName),
            new SqlQuerySpec()
            {
                QueryText = $"SELECT * FROM {_docDbMembershipsCollectionName} m WHERE m.code = @code",
                Parameters = new SqlParameterCollection()
                {
                    new SqlParameter("@code", code.ToUpper())
                }
            }, queryOptions).AsDocumentQuery();

List<dynamic> items = new List<dynamic>();
while (query.HasMoreResults)
{
    var queryResult = await query.ExecuteNextAsync();
    cost += queryResult.RequestCharge;
    items.AddRange(queryResult.ToList());
}

var omni = new DigitalMembershipOmni();
foreach (var item in items)
{
    var collectionType = (MembershipCollectionTypes)item.collectionType;
    if (collectionType == MembershipCollectionTypes.Membership)
    {
        omni.Clone((DigitalMembership)item);
    }
    else if (collectionType == MembershipCollectionTypes.RefreshItem)
    {
        omni.RefreshItems.Add((DigitalMembershipRefreshItem)item);
    }
    else if (collectionType == MembershipCollectionTypes.OfferItem)
    {
        omni.OfferItems.Add((DigitalMembershipOfferItem)item);
    }
    else if (collectionType == MembershipCollectionTypes.TrackingTransactionItem)
    {
        omni.TrackingTransactionItems.Add((DigitalMembershipTrackingTransactionItem)item);
    }
    else if (collectionType == MembershipCollectionTypes.BookingTransactionItem)
    {
        omni.BookingTransactionItems.Add((DigitalMembershipBookingTransactionItem)item);
    }
    else if (collectionType == MembershipCollectionTypes.MembershipTransactionItem)
    {
        omni.MembershipTransactionItems.Add((DigitalMembershipTransactionItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.NotificationItem)
    {
        omni.NotificationItems.Add((DigitalMembershipNotificationItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.CommentItem)
    {
        omni.CommentItems.Add((DigitalMembershipCommentItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.ContactUsItem)
    {
        omni.ContactUsItems.Add((DigitalMembershipContactUsItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.ReferralItem)
    {
        omni.ReferralItems.Add((DigitalMembershipReferralItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.EcertPinItem)
    {
        omni.EcertPinItems.Add((DigitalMembershipEcertPinItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.ProfileUpdateItem)
    {
        omni.ProfileUpdateItems.Add((DigitalMembershipProfileUpdateItem)item);
    }
}

return omni;

Вышеописанное работает .... но это дорого ... и ... оно плохо масштабируется .... поэтому, если членство накапливает больше данных, стоимость значительно возрастет. Я попытался сделать запрос для каждого типа, указав лучшие элементы для создания, такие как топ-100 уведомлений ... но порядок по upsertDate (как отмечено выше) делает запросы очень дорогими .... поэтому общая стоимость даже наихудший. Как можно достичь чего-то подобного с разумной стоимостью и производительностью.

ВОПРОС 4: У меня есть свойство membershipCode в коллекциях vouchers и discounts, как я могу выполнить запрос JOIN, который собирает ваучеры, принадлежащие определенному членству, не исчерпывая мои RU и не убивая моего спектакль? Я думаю, что запросы JOIN закончат сканирование всей коллекции vouchers с разными разделами и могут иметь значительные затраты и проблемы с пропускной способностью. Единственный способ заставить его работать достаточно хорошо - это использовать Table DB для связи vouchers с memberships. Запросы к Таблице БД выполняются быстро ... но это добавило существенное усложнение для моего небольшого проекта.

Я ценю любой указатель.

С уважением

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...