diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 07a64592c1..c51bbc27f7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -635,7 +635,7 @@ namespace Umbraco.Core.Persistence.Repositories return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, - "SELECT cmsDocument.nodeId", + new Tuple("cmsDocument", "nodeId"), ProcessQuery, orderBy, orderDirection, filterCallback); diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 6032ac926b..cda663b9f2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -388,7 +388,7 @@ namespace Umbraco.Core.Persistence.Repositories } return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, - "SELECT cmsContentVersion.contentId", + new Tuple("cmsContentVersion", "contentId"), ProcessQuery, orderBy, orderDirection, filterCallback); diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index c4fb8d0fd0..065849af64 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -584,7 +584,7 @@ namespace Umbraco.Core.Persistence.Repositories } return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, - "SELECT cmsMember.nodeId", + new Tuple("cmsMember", "nodeId"), ProcessQuery, orderBy, orderDirection, filterCallback); } diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index cbd35b7366..1013fbd634 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -225,6 +225,40 @@ namespace Umbraco.Core.Persistence.Repositories } } + private Sql GetFilteredSqlForPagedResults(Sql sql, Func> defaultFilter = null) + { + //copy to var so that the original isn't changed + var filteredSql = new Sql(sql.SQL, sql.Arguments); + // Apply filter + if (defaultFilter != null) + { + var filterResult = defaultFilter(); + filteredSql.Append(filterResult.Item1, filterResult.Item2); + } + return filteredSql; + } + + private Sql GetSortedSqlForPagedResults(Sql sql, Direction orderDirection, string orderBy) + { + //copy to var so that the original isn't changed + var sortedSql = new Sql(sql.SQL, sql.Arguments); + // Apply order according to parameters + if (string.IsNullOrEmpty(orderBy) == false) + { + var orderByParams = new[] { GetDatabaseFieldNameForOrderBy(orderBy) }; + if (orderDirection == Direction.Ascending) + { + sortedSql.OrderBy(orderByParams); + } + else + { + sortedSql.OrderByDescending(orderByParams); + } + return sortedSql; + } + return sortedSql; + } + /// /// A helper method for inheritors to get the paged results by query in a way that minimizes queries /// @@ -234,7 +268,7 @@ namespace Umbraco.Core.Persistence.Repositories /// Index of the page. /// Size of the page. /// The total records. - /// The SQL select statement fragment to return the node id from the query + /// The tablename + column name for the SELECT statement fragment to return the node id from the query /// A callback to create the default filter to be applied if there is one /// A callback to process the query result /// The order by column @@ -242,7 +276,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// orderBy protected IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, - string nodeIdSelect, + Tuple nodeIdSelect, Func> processQuery, string orderBy, Direction orderDirection, @@ -257,51 +291,18 @@ namespace Umbraco.Core.Persistence.Repositories if (query == null) query = new Query(); var translator = new SqlTranslator(sqlBase, query); var sqlQuery = translator.Translate(); - - Func getFilteredSql = (sql, additionalFilter, additionalFilterArgs) => - { - //copy to var so that the original isn't changed - var filteredSql = new Sql(sql.SQL, sql.Arguments); - // Apply filter - if (defaultFilter != null) - { - var filterResult = defaultFilter(); - filteredSql.Append(filterResult.Item1, filterResult.Item2); - } - if (string.IsNullOrEmpty(additionalFilter) == false) - { - filteredSql.Append("AND (" + additionalFilter + ")", additionalFilterArgs); - } - return filteredSql; - }; - - Func getSortedSql = sql => - { - //copy to var so that the original isn't changed - var sortedSql = new Sql(sql.SQL, sql.Arguments); - // Apply order according to parameters - if (string.IsNullOrEmpty(orderBy) == false) - { - var orderByParams = new[] { GetDatabaseFieldNameForOrderBy(orderBy) }; - if (orderDirection == Direction.Ascending) - { - sortedSql.OrderBy(orderByParams); - } - else - { - sortedSql.OrderByDescending(orderByParams); - } - return sortedSql; - } - return sortedSql; - }; - + // Note we can't do multi-page for several DTOs like we can multi-fetch and are doing in PerformGetByQuery, // but actually given we are doing a Get on each one (again as in PerformGetByQuery), we only need the node Id. // So we'll modify the SQL. - var sqlNodeIds = new Sql(sqlQuery.SQL.Replace("SELECT *", nodeIdSelect), sqlQuery.Arguments); - - var sqlNodeIdsWithSort = getSortedSql(getFilteredSql(sqlNodeIds, null, new object[0])); + var sqlNodeIds = new Sql( + sqlQuery.SQL.Replace("SELECT *", string.Format("SELECT {0}.{1}",nodeIdSelect.Item1, nodeIdSelect.Item2)), + sqlQuery.Arguments); + + //get sorted and filtered sql + var sqlNodeIdsWithSort = GetSortedSqlForPagedResults( + GetFilteredSqlForPagedResults(sqlNodeIds, defaultFilter), + orderDirection, orderBy); // Get page of results and total count IEnumerable result; @@ -314,19 +315,29 @@ namespace Umbraco.Core.Persistence.Repositories string sqlStringCount, sqlStringPage; Database.BuildPageQueries(pageIndex * pageSize, pageSize, sqlNodeIdsWithSort.SQL, ref args, out sqlStringCount, out sqlStringPage); - //we now need to finalize/parse this query so that the args are built in to it, we know the args will only be two for this operation - //sqlPage = sqlPage.Replace("@0", args[0].ToString()).Replace("@1", args[1].ToString()); + //if this is for sql server, the sqlPage will start with a SELECT * but we don't want that, we only want to return the nodeId + sqlStringPage = sqlStringPage + .Replace("SELECT *", + //This ensures we only take the field name of the node id select and not the table name - since the resulting select + // will ony work with the field name. + "SELECT " + nodeIdSelect.Item2); - //if this is for sql server, the sqlPage will start with a SELECT * but we don't want that, we only want to return the nodeId - var nodeIdSelectParts = nodeIdSelect.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries); - sqlStringPage = sqlStringPage.Replace("SELECT *", - //This ensures we only take the field name of the node id select and not the table name - since the resulting select - // will ony work with the field name. - "SELECT " + nodeIdSelectParts[nodeIdSelectParts.Length - 1]); - - var fullQuery = getSortedSql( - getFilteredSql(sqlQuery, string.Format("umbracoNode.id IN ({0})", sqlStringPage), args)); + //We need to make this an inner join on the paged query + var splitQuery = sqlQuery.SQL.Split(new[] {"WHERE "}, StringSplitOptions.None); + var withInnerJoinSql = new Sql(splitQuery[0]) + .Append("INNER JOIN (") + //join the paged query with the paged query arguments + .Append(sqlStringPage, args) + .Append(") temp ") + .Append(string.Format("ON {0}.{1} = temp.{1}", nodeIdSelect.Item1, nodeIdSelect.Item2)) + //add the original where clause back with the original arguments + .Where(splitQuery[1], sqlQuery.Arguments); + //get sorted and filtered sql + var fullQuery = GetSortedSqlForPagedResults( + GetFilteredSqlForPagedResults(withInnerJoinSql, defaultFilter), + orderDirection, orderBy); + var content = processQuery(fullQuery) .Cast() .AsQueryable(); diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs index 2f253855f3..730395d35a 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs @@ -67,18 +67,22 @@ namespace Umbraco.Web.PropertyEditors } //set the multiPicker val correctly depending on the maxNumber - var asNumber = result["maxNumber"].TryConvertTo(); - if (asNumber.Success) + if (result.ContainsKey("maxNumber")) { - if (asNumber.Result <= 1) + var asNumber = result["maxNumber"].TryConvertTo(); + if (asNumber.Success) { - result["multiPicker"] = "0"; - } - else - { - result["multiPicker"] = "1"; - } + if (asNumber.Result <= 1) + { + result["multiPicker"] = "0"; + } + else + { + result["multiPicker"] = "1"; + } + } } + return result; } diff --git a/src/UmbracoExamine/UmbracoMemberIndexer.cs b/src/UmbracoExamine/UmbracoMemberIndexer.cs index 83652ecd13..9f3496fb6d 100644 --- a/src/UmbracoExamine/UmbracoMemberIndexer.cs +++ b/src/UmbracoExamine/UmbracoMemberIndexer.cs @@ -29,11 +29,14 @@ namespace UmbracoExamine private readonly IMemberService _memberService; private readonly IDataTypeService _dataTypeService; - /// - /// Default constructor - /// - public UmbracoMemberIndexer() - : base() { } + /// + /// Default constructor + /// + public UmbracoMemberIndexer() : base() + { + _dataTypeService = ApplicationContext.Current.Services.DataTypeService; + _memberService = ApplicationContext.Current.Services.MemberService; + } /// /// Constructor to allow for creating an indexer at runtime