Skip to content

Commit 379d4fe

Browse files
authored
Merge pull request #33 from cloudscribe/feature/7
#7 merge back to master (belatedly)
2 parents 6efc940 + b620e68 commit 379d4fe

22 files changed

Lines changed: 505 additions & 42 deletions
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Author: Joe Audette
4+
// Created: 2017-07-13
5+
// Last Modified: 2018-10-08
6+
//
7+
8+
using cloudscribe.Core.Models;
9+
using cloudscribe.Pagination.Models;
10+
using System;
11+
using System.Collections.Generic;
12+
using System.Threading;
13+
using System.Threading.Tasks;
14+
15+
namespace cloudscribe.Kvp.Models
16+
{
17+
public interface IKvpUserSearchQueries
18+
{
19+
Task<int> CountUsersForAdminSearch(
20+
Guid siteId,
21+
string[] searchTerms,
22+
CancellationToken cancellationToken = default(CancellationToken));
23+
24+
25+
Task<PagedResult<IUserInfo>> GetUserAdminSearchPage(
26+
Guid siteId,
27+
int pageNumber,
28+
int pageSize,
29+
string searchInput,
30+
int sortMode,
31+
List<string> searchableKvpKeys,
32+
CancellationToken cancellationToken = default);
33+
}
34+
}

src/cloudscribe.Kvp.Models/cloudscribe.Kvp.Models.csproj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
<PropertyGroup>
44
<Description>model classes for key/value storage</Description>
5-
<Version>4.1.0</Version>
5+
<Version>4.1.2</Version>
66
<Authors>Joe Audette</Authors>
7-
<TargetFrameworks>netstandard1.6</TargetFrameworks>
7+
<TargetFrameworks>netstandard2.1</TargetFrameworks>
88
<PackageTags>cloudscribe;kvp</PackageTags>
99
<PackageIcon>icon.png</PackageIcon>
1010
<PackageProjectUrl>https://github.com/cloudscribe/cloudscribe.UserProperties.Kvp</PackageProjectUrl>
@@ -19,5 +19,9 @@
1919
<None Include="icon.png" Pack="true" PackagePath="\"/>
2020
</ItemGroup>
2121

22-
22+
<ItemGroup>
23+
<PackageReference Include="cloudscribe.Core.Models" Version="4.1.5" />
24+
<PackageReference Include="cloudscribe.Pagination.Models" Version="1.1.0" />
25+
</ItemGroup>
26+
2327
</Project>
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Author: Joe Audette
4+
// Created: 2017-07-13
5+
// Last Modified: 2020-12-16 - jk
6+
//
7+
8+
using cloudscribe.Core.Models;
9+
using cloudscribe.Core.Storage.EFCore.Common;
10+
using cloudscribe.Kvp.Models;
11+
using cloudscribe.Pagination.Models;
12+
using Microsoft.EntityFrameworkCore;
13+
using System;
14+
using System.Collections.Generic;
15+
using System.Linq;
16+
using System.Threading;
17+
using System.Threading.Tasks;
18+
19+
namespace cloudscribe.Kvp.Storage.EFCore.Common
20+
{
21+
public class KvpUserSearchQueries : IKvpUserSearchQueries
22+
{
23+
public KvpUserSearchQueries(IKvpDbContextFactory kvpContextFactory,
24+
ICoreDbContextFactory coreDbContextFactory)
25+
{
26+
_kvpContextFactory = kvpContextFactory;
27+
_coreDbContextFactory = coreDbContextFactory;
28+
}
29+
30+
private readonly IKvpDbContextFactory _kvpContextFactory;
31+
private readonly ICoreDbContextFactory _coreDbContextFactory;
32+
33+
private Dictionary<string, List<string>> kvpMatches = new Dictionary<string, List<string>>();
34+
35+
public async Task<PagedResult<IUserInfo>> GetUserAdminSearchPage(
36+
Guid siteId,
37+
int pageNumber,
38+
int pageSize,
39+
string searchInput,
40+
int sortMode,
41+
List<string> searchableKvpKeys,
42+
CancellationToken cancellationToken = default(CancellationToken))
43+
{
44+
//sortMode: 0 = DisplayName asc, 1 = JoinDate desc, 2 = Last, First
45+
46+
if (searchInput == null) searchInput = string.Empty;
47+
48+
//allows user to enter multiple words (e.g. to allow full name search)
49+
var searchTerms = searchInput.Trim().ToUpper().Split(" ");
50+
51+
int offset = (pageSize * pageNumber) - pageSize;
52+
53+
string searchInputUpper = searchInput.Trim().ToUpper();
54+
55+
// get a list of user IDs from KVPs that match each search input term
56+
// provided the KVP is configured searchable
57+
using (var dbKvpContext = _kvpContextFactory.Create())
58+
{
59+
foreach (var term in searchTerms)
60+
{
61+
if (!string.IsNullOrWhiteSpace(term))
62+
{
63+
var usersMatchingTerm = dbKvpContext.KvpItems
64+
.Where (x => searchableKvpKeys.Contains(x.Key.ToUpper()))
65+
.Where (x => x.SetId.ToUpper().Equals(siteId.ToString().ToUpper()))
66+
.Where (x => x.Value.ToUpper().Contains(term))
67+
.Select(x => x.SubSetId.ToUpper()).Distinct().ToList();
68+
69+
kvpMatches.Add(term, usersMatchingTerm);
70+
}
71+
}
72+
}
73+
74+
using (var dbContext = _coreDbContextFactory.CreateContext())
75+
{
76+
var query = dbContext.Users.Where(x => x.SiteId == siteId);
77+
foreach (var term in searchTerms)
78+
{
79+
if (!string.IsNullOrWhiteSpace(term))
80+
{
81+
// Note each term is already in upper case
82+
query = query.Where(x =>
83+
x.NormalizedEmail.Contains(term)
84+
|| x.NormalizedUserName.Contains(term)
85+
|| (x.FirstName != null && x.FirstName.ToUpper().Contains(term))
86+
|| (x.LastName != null && x.LastName.ToUpper().Contains(term))
87+
|| x.DisplayName.ToUpper().Contains(term)
88+
|| kvpMatches[term].Contains(x.Id.ToString().ToUpper())
89+
);
90+
}
91+
}
92+
93+
query = query.Distinct();
94+
95+
switch (sortMode)
96+
{
97+
case 2:
98+
query = query.OrderBy(sl => sl.LastName).ThenBy(s2 => s2.FirstName).AsQueryable();
99+
break;
100+
case 1:
101+
query = query.OrderByDescending(sl => sl.CreatedUtc).AsQueryable();
102+
break;
103+
case 0:
104+
default:
105+
query = query.OrderBy(sl => sl.DisplayName).AsQueryable();
106+
break;
107+
}
108+
109+
var data = await query
110+
.AsNoTracking()
111+
.Skip(offset)
112+
.Take(pageSize)
113+
.ToListAsync<IUserInfo>(cancellationToken)
114+
.ConfigureAwait(false);
115+
116+
return new PagedResult<IUserInfo>
117+
{
118+
Data = data,
119+
PageNumber = pageNumber,
120+
PageSize = pageSize,
121+
TotalItems = await CountUsersForAdminSearch(siteId, searchTerms, cancellationToken).ConfigureAwait(false)
122+
};
123+
}
124+
}
125+
126+
127+
public async Task<int> CountUsersForAdminSearch(
128+
Guid siteId,
129+
string[] searchTerms,
130+
CancellationToken cancellationToken = default(CancellationToken))
131+
{
132+
using (var dbContext = _coreDbContextFactory.CreateContext())
133+
{
134+
var query = dbContext.Users.Where(x => x.SiteId == siteId);
135+
136+
foreach (var term in searchTerms)
137+
{
138+
if (!string.IsNullOrWhiteSpace(term))
139+
{
140+
query = query.Where(x =>
141+
x.NormalizedEmail.Contains(term)
142+
|| x.NormalizedUserName.Contains(term)
143+
|| (x.FirstName != null && x.FirstName.ToUpper().Contains(term))
144+
|| (x.LastName != null && x.LastName .ToUpper().Contains(term))
145+
|| x.DisplayName.ToUpper().Contains(term)
146+
|| kvpMatches[term].Contains(x.Id.ToString().ToUpper())
147+
);
148+
}
149+
}
150+
151+
query = query.Distinct();
152+
153+
return await query.CountAsync().ConfigureAwait(false);
154+
}
155+
}
156+
}
157+
}

src/cloudscribe.Kvp.Storage.EFCore.Common/StartupExtensions.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using cloudscribe.Kvp.Models;
22
using Microsoft.Extensions.DependencyInjection;
3-
using Microsoft.Extensions.DependencyInjection.Extensions;
43

54
namespace cloudscribe.Kvp.Storage.EFCore.Common
65
{
@@ -10,9 +9,9 @@ public static IServiceCollection AddCloudscribeKvpEFCommon(
109
this IServiceCollection services
1110
)
1211
{
13-
1412
services.AddScoped<IKvpItemQueries, KvpItemQueries>();
1513
services.AddScoped<IKvpItemCommands, KvpItemCommands>();
14+
services.AddScoped<IKvpUserSearchQueries, KvpUserSearchQueries>();
1615

1716
return services;
1817
}

src/cloudscribe.Kvp.Storage.EFCore.Common/cloudscribe.Kvp.Storage.EFCore.Common.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>Entity Framework Core common classes for cloudscribe.Kvp</Description>
5-
<Version>4.1.0</Version>
5+
<Version>4.1.2</Version>
66
<Authors>Joe Audette</Authors>
77
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
88
<PackageTags>cloudscribe;kvp;commands;queries;ef</PackageTags>
@@ -25,6 +25,7 @@
2525
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
2626
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.0" />
2727
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.0" />
28+
<PackageReference Include="cloudscribe.Core.Storage.EFCore.Common" Version="4.1.5" />
2829
</ItemGroup>
2930

3031
</Project>

src/cloudscribe.Kvp.Storage.EFCore.MSSQL/cloudscribe.Kvp.Storage.EFCore.MSSQL.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>Entity Framework Core common classes for cloudscribe.Kvp</Description>
5-
<Version>4.1.0</Version>
5+
<Version>4.1.2</Version>
66
<Authors>Joe Audette</Authors>
77
<TargetFramework>netcoreapp3.1</TargetFramework>
88
<PackageTags>cloudscribe;kvp;commands;queries;ef</PackageTags>

src/cloudscribe.Kvp.Storage.EFCore.MySql/cloudscribe.Kvp.Storage.EFCore.MySql.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>MySql Entity Framework Core storage for cloudscribe.Kvp</Description>
5-
<Version>4.1.0</Version>
5+
<Version>4.1.2</Version>
66
<Authors>Joe Audette</Authors>
77
<TargetFramework>netcoreapp3.1</TargetFramework>
88
<PackageTags>cloudscribe;kvp;commands;queries;ef</PackageTags>

src/cloudscribe.Kvp.Storage.EFCore.PostgreSql/cloudscribe.Kvp.Storage.EFCore.PostgreSql.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>Entity Framework Core postgresql storage for cloudscribe.Kvp</Description>
5-
<Version>4.1.0</Version>
5+
<Version>4.1.2</Version>
66
<Authors>Joe Audette</Authors>
77
<TargetFramework>netcoreapp3.1</TargetFramework>
88
<PackageTags>cloudscribe;kvp;commands;ef</PackageTags>

src/cloudscribe.Kvp.Storage.EFCore.SQLite/cloudscribe.Kvp.Storage.EFCore.SQLite.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>Entity Framework Core SQLite storage for cloudscribe.Kvp</Description>
5-
<Version>4.1.0</Version>
5+
<Version>4.1.2</Version>
66
<Authors>Joe Audette</Authors>
77
<TargetFramework>netcoreapp3.1</TargetFramework>
88
<PackageTags>cloudscribe;kvp;ef</PackageTags>

src/cloudscribe.Kvp.Storage.EFCore.pgsql/cloudscribe.Kvp.Storage.EFCore.pgsql.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>Entity Framework Core postgresql storage for cloudscribe.Kvp</Description>
5-
<Version>4.1.0</Version>
5+
<Version>4.1.2</Version>
66
<Authors>Joe Audette</Authors>
77
<TargetFramework>netcoreapp3.1</TargetFramework>
88
<PackageTags>cloudscribe;kvp;commands;ef</PackageTags>

0 commit comments

Comments
 (0)