Merge pull request #7067 from umbraco/v8/feature/AB3466-paged-relations

Adds ability to get paged relations by type
This commit is contained in:
Warren Buckley
2019-11-11 14:03:34 +00:00
committed by GitHub
6 changed files with 190 additions and 66 deletions

View File

@@ -2,11 +2,14 @@
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IRelationRepository : IReadWriteQueryRepository<int, IRelation>
{
IEnumerable<IRelation> GetPagedRelationsByQuery(IQuery<IRelation> query, long pageIndex, int pageSize, out long totalRecords, Ordering ordering);
/// <summary>
/// Persist multiple <see cref="IRelation"/> at once
/// </summary>

View File

@@ -516,11 +516,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// TODO: although the default ordering string works for name, it wont work for others without a table or an alias of some sort
// As more things are attempted to be sorted we'll prob have to add more expressions here
var orderBy = ordering.OrderBy.ToUpperInvariant() switch
string orderBy;
switch (ordering.OrderBy.ToUpperInvariant())
{
"PATH" => SqlSyntax.GetQuotedColumn(NodeDto.TableName, "path"),
_ => ordering.OrderBy
};
case "PATH":
orderBy = SqlSyntax.GetQuotedColumn(NodeDto.TableName, "path");
break;
default:
orderBy = ordering.OrderBy;
break;
}
if (ordering.Direction == Direction.Ascending)
sql.OrderBy(orderBy);

View File

@@ -6,6 +6,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
@@ -239,6 +240,38 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
}
}
public IEnumerable<IRelation> GetPagedRelationsByQuery(IQuery<IRelation> query, long pageIndex, int pageSize, out long totalRecords, Ordering ordering)
{
var sql = GetBaseQuery(false);
if (ordering == null || ordering.IsEmpty)
ordering = Ordering.By(SqlSyntax.GetQuotedColumn(Constants.DatabaseSchema.Tables.Relation, "id"));
var translator = new SqlTranslator<IRelation>(sql, query);
sql = translator.Translate();
// apply ordering
ApplyOrdering(ref sql, ordering);
var pageIndexToFetch = pageIndex + 1;
var page = Database.Page<RelationDto>(pageIndexToFetch, pageSize, sql);
var dtos = page.Items;
totalRecords = page.TotalItems;
var relTypes = _relationTypeRepository.GetMany(dtos.Select(x => x.RelationType).Distinct().ToArray())
.ToDictionary(x => x.Id, x => x);
var result = dtos.Select(r =>
{
if (!relTypes.TryGetValue(r.RelationType, out var relType))
throw new InvalidOperationException(string.Format("RelationType with Id: {0} doesn't exist", r.RelationType));
return DtoToEntity(r, relType);
}).ToList();
return result;
}
public void DeleteByParent(int parentId, params string[] relationTypeAliases)
{
var subQuery = Sql().Select<RelationDto>(x => x.Id)
@@ -278,5 +311,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
}
}
}
private void ApplyOrdering(ref Sql<ISqlContext> sql, Ordering ordering)
{
if (sql == null) throw new ArgumentNullException(nameof(sql));
if (ordering == null) throw new ArgumentNullException(nameof(ordering));
// TODO: although this works for name, it probably doesn't work for others without an alias of some sort
var orderBy = ordering.OrderBy;
if (ordering.Direction == Direction.Ascending)
sql.OrderBy(orderBy);
else
sql.OrderByDescending(orderBy);
}
}
}

View File

@@ -8,152 +8,162 @@ namespace Umbraco.Core.Services
public interface IRelationService : IService
{
/// <summary>
/// Gets a <see cref="Relation"/> by its Id
/// Gets a <see cref="IRelation"/> by its Id
/// </summary>
/// <param name="id">Id of the <see cref="Relation"/></param>
/// <returns>A <see cref="Relation"/> object</returns>
/// <param name="id">Id of the <see cref="IRelation"/></param>
/// <returns>A <see cref="IRelation"/> object</returns>
IRelation GetById(int id);
/// <summary>
/// Gets a <see cref="RelationType"/> by its Id
/// Gets a <see cref="IRelationType"/> by its Id
/// </summary>
/// <param name="id">Id of the <see cref="RelationType"/></param>
/// <returns>A <see cref="RelationType"/> object</returns>
/// <param name="id">Id of the <see cref="IRelationType"/></param>
/// <returns>A <see cref="IRelationType"/> object</returns>
IRelationType GetRelationTypeById(int id);
/// <summary>
/// Gets a <see cref="RelationType"/> by its Id
/// Gets a <see cref="IRelationType"/> by its Id
/// </summary>
/// <param name="id">Id of the <see cref="RelationType"/></param>
/// <returns>A <see cref="RelationType"/> object</returns>
/// <param name="id">Id of the <see cref="IRelationType"/></param>
/// <returns>A <see cref="IRelationType"/> object</returns>
IRelationType GetRelationTypeById(Guid id);
/// <summary>
/// Gets a <see cref="RelationType"/> by its Alias
/// Gets a <see cref="IRelationType"/> by its Alias
/// </summary>
/// <param name="alias">Alias of the <see cref="RelationType"/></param>
/// <returns>A <see cref="RelationType"/> object</returns>
/// <param name="alias">Alias of the <see cref="IRelationType"/></param>
/// <returns>A <see cref="IRelationType"/> object</returns>
IRelationType GetRelationTypeByAlias(string alias);
/// <summary>
/// Gets all <see cref="Relation"/> objects
/// Gets all <see cref="IRelation"/> objects
/// </summary>
/// <param name="ids">Optional array of integer ids to return relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetAllRelations(params int[] ids);
/// <summary>
/// Gets all <see cref="Relation"/> objects by their <see cref="RelationType"/>
/// Gets all <see cref="IRelation"/> objects by their <see cref="IRelationType"/>
/// </summary>
/// <param name="relationType"><see cref="RelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <param name="relationType"><see cref="IRelation"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetAllRelationsByRelationType(IRelationType relationType);
/// <summary>
/// Gets all <see cref="Relation"/> objects by their <see cref="RelationType"/>'s Id
/// Gets all <see cref="IRelation"/> objects by their <see cref="IRelationType"/>'s Id
/// </summary>
/// <param name="relationTypeId">Id of the <see cref="RelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <param name="relationTypeId">Id of the <see cref="IRelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetAllRelationsByRelationType(int relationTypeId);
/// <summary>
/// Gets all <see cref="Relation"/> objects
/// Gets all <see cref="IRelation"/> objects
/// </summary>
/// <param name="ids">Optional array of integer ids to return relationtypes for</param>
/// <returns>An enumerable list of <see cref="RelationType"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelationType> GetAllRelationTypes(params int[] ids);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their parent Id
/// Gets a list of <see cref="IRelation"/> objects by their parent Id
/// </summary>
/// <param name="id">Id of the parent to retrieve relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByParentId(int id);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their parent Id
/// Gets a list of <see cref="IRelation"/> objects by their parent Id
/// </summary>
/// <param name="id">Id of the parent to retrieve relations for</param>
/// <param name="relationTypeAlias">Alias of the type of relation to retrieve</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByParentId(int id, string relationTypeAlias);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their parent entity
/// Gets a list of <see cref="IRelation"/> objects by their parent entity
/// </summary>
/// <param name="parent">Parent Entity to retrieve relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByParent(IUmbracoEntity parent);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their parent entity
/// Gets a list of <see cref="IRelation"/> objects by their parent entity
/// </summary>
/// <param name="parent">Parent Entity to retrieve relations for</param>
/// <param name="relationTypeAlias">Alias of the type of relation to retrieve</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByParent(IUmbracoEntity parent, string relationTypeAlias);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their child Id
/// Gets a list of <see cref="IRelation"/> objects by their child Id
/// </summary>
/// <param name="id">Id of the child to retrieve relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByChildId(int id);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their child Id
/// Gets a list of <see cref="IRelation"/> objects by their child Id
/// </summary>
/// <param name="id">Id of the child to retrieve relations for</param>
/// <param name="relationTypeAlias">Alias of the type of relation to retrieve</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByChildId(int id, string relationTypeAlias);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their child Entity
/// Gets a list of <see cref="IRelation"/> objects by their child Entity
/// </summary>
/// <param name="child">Child Entity to retrieve relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByChild(IUmbracoEntity child);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their child Entity
/// Gets a list of <see cref="IRelation"/> objects by their child Entity
/// </summary>
/// <param name="child">Child Entity to retrieve relations for</param>
/// <param name="relationTypeAlias">Alias of the type of relation to retrieve</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByChild(IUmbracoEntity child, string relationTypeAlias);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by their child or parent Id.
/// Gets a list of <see cref="IRelation"/> objects by their child or parent Id.
/// Using this method will get you all relations regards of it being a child or parent relation.
/// </summary>
/// <param name="id">Id of the child or parent to retrieve relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByParentOrChildId(int id);
IEnumerable<IRelation> GetByParentOrChildId(int id, string relationTypeAlias);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by the Name of the <see cref="RelationType"/>
/// Gets a list of <see cref="IRelation"/> objects by the Name of the <see cref="IRelationType"/>
/// </summary>
/// <param name="relationTypeName">Name of the <see cref="RelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <param name="relationTypeName">Name of the <see cref="IRelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByRelationTypeName(string relationTypeName);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by the Alias of the <see cref="RelationType"/>
/// Gets a list of <see cref="IRelation"/> objects by the Alias of the <see cref="IRelationType"/>
/// </summary>
/// <param name="relationTypeAlias">Alias of the <see cref="RelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <param name="relationTypeAlias">Alias of the <see cref="IRelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByRelationTypeAlias(string relationTypeAlias);
/// <summary>
/// Gets a list of <see cref="Relation"/> objects by the Id of the <see cref="RelationType"/>
/// Gets a list of <see cref="IRelation"/> objects by the Id of the <see cref="IRelationType"/>
/// </summary>
/// <param name="relationTypeId">Id of the <see cref="RelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
/// <param name="relationTypeId">Id of the <see cref="IRelationType"/> to retrieve Relations for</param>
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
IEnumerable<IRelation> GetByRelationTypeId(int relationTypeId);
/// <summary>
/// Gets a paged result of <see cref="IRelation"/>
/// </summary>
/// <param name="relationTypeId"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalChildren"></param>
/// <returns></returns>
IEnumerable<IRelation> GetPagedByRelationTypeId(int relationTypeId, long pageIndex, int pageSize, out long totalRecords, Ordering ordering = null);
/// <summary>
/// Gets the Child object from a Relation as an <see cref="IUmbracoEntity"/>
/// </summary>
@@ -222,7 +232,7 @@ namespace Umbraco.Core.Services
/// <param name="parentId">Id of the parent</param>
/// <param name="childId">Id of the child</param>
/// <param name="relationType">The type of relation to create</param>
/// <returns>The created <see cref="Relation"/></returns>
/// <returns>The created <see cref="IRelation"/></returns>
IRelation Relate(int parentId, int childId, IRelationType relationType);
/// <summary>
@@ -231,7 +241,7 @@ namespace Umbraco.Core.Services
/// <param name="parent">Parent entity</param>
/// <param name="child">Child entity</param>
/// <param name="relationType">The type of relation to create</param>
/// <returns>The created <see cref="Relation"/></returns>
/// <returns>The created <see cref="IRelation"/></returns>
IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, IRelationType relationType);
/// <summary>
@@ -240,7 +250,7 @@ namespace Umbraco.Core.Services
/// <param name="parentId">Id of the parent</param>
/// <param name="childId">Id of the child</param>
/// <param name="relationTypeAlias">Alias of the type of relation to create</param>
/// <returns>The created <see cref="Relation"/></returns>
/// <returns>The created <see cref="IRelation"/></returns>
IRelation Relate(int parentId, int childId, string relationTypeAlias);
/// <summary>
@@ -249,14 +259,14 @@ namespace Umbraco.Core.Services
/// <param name="parent">Parent entity</param>
/// <param name="child">Child entity</param>
/// <param name="relationTypeAlias">Alias of the type of relation to create</param>
/// <returns>The created <see cref="Relation"/></returns>
/// <returns>The created <see cref="IRelation"/></returns>
IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias);
/// <summary>
/// Checks whether any relations exists for the passed in <see cref="RelationType"/>.
/// Checks whether any relations exists for the passed in <see cref="IRelationType"/>.
/// </summary>
/// <param name="relationType"><see cref="RelationType"/> to check for relations</param>
/// <returns>Returns <c>True</c> if any relations exists for the given <see cref="RelationType"/>, otherwise <c>False</c></returns>
/// <param name="relationType"><see cref="IRelationType"/> to check for relations</param>
/// <returns>Returns <c>True</c> if any relations exists for the given <see cref="IRelationType"/>, otherwise <c>False</c></returns>
bool HasRelations(IRelationType relationType);
/// <summary>
@@ -301,7 +311,7 @@ namespace Umbraco.Core.Services
bool AreRelated(int parentId, int childId, string relationTypeAlias);
/// <summary>
/// Saves a <see cref="Relation"/>
/// Saves a <see cref="IRelation"/>
/// </summary>
/// <param name="relation">Relation to save</param>
void Save(IRelation relation);
@@ -309,27 +319,27 @@ namespace Umbraco.Core.Services
void Save(IEnumerable<IRelation> relations);
/// <summary>
/// Saves a <see cref="RelationType"/>
/// Saves a <see cref="IRelationType"/>
/// </summary>
/// <param name="relationType">RelationType to Save</param>
void Save(IRelationType relationType);
/// <summary>
/// Deletes a <see cref="Relation"/>
/// Deletes a <see cref="IRelation"/>
/// </summary>
/// <param name="relation">Relation to Delete</param>
void Delete(IRelation relation);
/// <summary>
/// Deletes a <see cref="RelationType"/>
/// Deletes a <see cref="IRelationType"/>
/// </summary>
/// <param name="relationType">RelationType to Delete</param>
void Delete(IRelationType relationType);
/// <summary>
/// Deletes all <see cref="Relation"/> objects based on the passed in <see cref="RelationType"/>
/// Deletes all <see cref="IRelation"/> objects based on the passed in <see cref="IRelationType"/>
/// </summary>
/// <param name="relationType"><see cref="RelationType"/> to Delete Relations for</param>
/// <param name="relationType"><see cref="IRelationType"/> to Delete Relations for</param>
void DeleteRelationsOfType(IRelationType relationType);
}
}

View File

@@ -207,6 +207,16 @@ namespace Umbraco.Core.Services.Implement
}
}
/// <inheritdoc />
public IEnumerable<IRelation> GetPagedByRelationTypeId(int relationTypeId, long pageIndex, int pageSize, out long totalRecords, Ordering ordering = null)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
var query = Query<IRelation>().Where(x => x.RelationTypeId == relationTypeId);
return _relationRepository.GetPagedRelationsByQuery(query, pageIndex, pageSize, out totalRecords, ordering);
}
}
/// <inheritdoc />
public IUmbracoEntity GetChildEntityFromRelation(IRelation relation)
{

View File

@@ -16,6 +16,54 @@ namespace Umbraco.Tests.Services
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
public class RelationServiceTests : TestWithSomeContentBase
{
[Test]
public void Get_Paged_Relations_By_Relation_Type()
{
//Create content
var createdContent = new List<IContent>();
var contentType = MockedContentTypes.CreateBasicContentType("blah");
ServiceContext.ContentTypeService.Save(contentType);
for (int i = 0; i < 10; i++)
{
var c1 = MockedContent.CreateBasicContent(contentType);
ServiceContext.ContentService.Save(c1);
createdContent.Add(c1);
}
//Create media
var createdMedia = new List<IMedia>();
var imageType = MockedContentTypes.CreateImageMediaType("myImage");
ServiceContext.MediaTypeService.Save(imageType);
for (int i = 0; i < 10; i++)
{
var c1 = MockedMedia.CreateMediaImage(imageType, -1);
ServiceContext.MediaService.Save(c1);
createdMedia.Add(c1);
}
var relType = ServiceContext.RelationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelatedMediaAlias);
// Relate content to media
foreach (var content in createdContent)
foreach (var media in createdMedia)
ServiceContext.RelationService.Relate(content.Id, media.Id, relType);
var paged = ServiceContext.RelationService.GetPagedByRelationTypeId(relType.Id, 0, 51, out var totalRecs).ToList();
Assert.AreEqual(100, totalRecs);
Assert.AreEqual(51, paged.Count);
//next page
paged.AddRange(ServiceContext.RelationService.GetPagedByRelationTypeId(relType.Id, 1, 51, out totalRecs));
Assert.AreEqual(100, totalRecs);
Assert.AreEqual(100, paged.Count);
Assert.IsTrue(createdContent.Select(x => x.Id).ContainsAll(paged.Select(x => x.ParentId)));
Assert.IsTrue(createdMedia.Select(x => x.Id).ContainsAll(paged.Select(x => x.ChildId)));
}
[Test]
public void Return_List_Of_Content_Items_Where_Media_Item_Referenced()
{