├── .gitignore
├── .paket
├── Paket.Restore.targets
├── paket.exe
└── paket.targets
├── Directory.build.props
├── FluentNest.Tests
├── .gitignore
├── Benchmarking.cs
├── DeleteTests.cs
├── EnumTests.cs
├── FilterTests.cs
├── FilterTests.txt
├── FluentNest.Tests.csproj
├── GroupByTests.cs
├── HistogramTests.cs
├── Model
│ ├── Car.cs
│ ├── CarType.cs
│ └── User.cs
├── PropertyNamesTests.cs
├── StatisticsTests.cs
├── TestsBase.cs
├── app.config
├── packages.config
└── paket.references
├── FluentNest.sln
├── FluentNest
├── .gitignore
├── AggType.cs
├── AggsContainer.cs
├── Filters.cs
├── FluentNest.csproj
├── GroupBys.cs
├── Names.cs
├── Ranges.cs
├── SortType.cs
├── Statistics.cs
├── StatisticsGetters.cs
└── paket.references
├── LICENSE
├── README.md
├── appveyor.yml
├── build.cmd
├── global.json
├── paket.cmd
├── paket.dependencies
├── paket.lock
├── run_es.ps1
└── tests.cmd
/.gitignore:
--------------------------------------------------------------------------------
1 | es.zip
2 | /elasticsearch-*
3 | paket-files/*.cached
4 |
5 | ## Ignore Visual Studio temporary files, build results, and
6 | ## files generated by popular Visual Studio add-ons.
7 |
8 | # User-specific files
9 | *.suo
10 | *.user
11 | *.userosscache
12 | *.sln.docstates
13 |
14 | # User-specific files (MonoDevelop/Xamarin Studio)
15 | *.userprefs
16 |
17 | # Build results
18 | [Dd]ebug/
19 | [Dd]ebugPublic/
20 | [Rr]elease/
21 | [Rr]eleases/
22 | x64/
23 | x86/
24 | build/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 |
29 | # Visual Studo 2015 cache/options directory
30 | .vs/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | *_i.c
46 | *_p.c
47 | *_i.h
48 | *.ilk
49 | *.meta
50 | *.obj
51 | *.pch
52 | *.pdb
53 | *.pgc
54 | *.pgd
55 | *.rsp
56 | *.sbr
57 | *.tlb
58 | *.tli
59 | *.tlh
60 | *.tmp
61 | *.tmp_proj
62 | *.log
63 | *.vspscc
64 | *.vssscc
65 | .builds
66 | *.pidb
67 | *.svclog
68 | *.scc
69 |
70 | # Chutzpah Test files
71 | _Chutzpah*
72 |
73 | # Visual C++ cache files
74 | ipch/
75 | *.aps
76 | *.ncb
77 | *.opensdf
78 | *.sdf
79 | *.cachefile
80 |
81 | # Visual Studio profiler
82 | *.psess
83 | *.vsp
84 | *.vspx
85 |
86 | # TFS 2012 Local Workspace
87 | $tf/
88 |
89 | # Guidance Automation Toolkit
90 | *.gpState
91 |
92 | # ReSharper is a .NET coding add-in
93 | _ReSharper*/
94 | *.[Rr]e[Ss]harper
95 | *.DotSettings.user
96 |
97 | # JustCode is a .NET coding addin-in
98 | .JustCode
99 |
100 | # TeamCity is a build add-in
101 | _TeamCity*
102 |
103 | # DotCover is a Code Coverage Tool
104 | *.dotCover
105 |
106 | # NCrunch
107 | _NCrunch_*
108 | .*crunch*.local.xml
109 |
110 | # MightyMoose
111 | *.mm.*
112 | AutoTest.Net/
113 |
114 | # Web workbench (sass)
115 | .sass-cache/
116 |
117 | # Installshield output folder
118 | [Ee]xpress/
119 |
120 | # DocProject is a documentation generator add-in
121 | DocProject/buildhelp/
122 | DocProject/Help/*.HxT
123 | DocProject/Help/*.HxC
124 | DocProject/Help/*.hhc
125 | DocProject/Help/*.hhk
126 | DocProject/Help/*.hhp
127 | DocProject/Help/Html2
128 | DocProject/Help/html
129 |
130 | # Click-Once directory
131 | publish/
132 |
133 | # Publish Web Output
134 | *.[Pp]ublish.xml
135 | *.azurePubxml
136 | # TODO: Comment the next line if you want to checkin your web deploy settings
137 | # but database connection strings (with potential passwords) will be unencrypted
138 | *.pubxml
139 | *.publishproj
140 |
141 | # NuGet Packages
142 | *.nupkg
143 | # The packages folder can be ignored because of Package Restore
144 | **/packages/*
145 | # except build/, which is used as an MSBuild target.
146 | !**/packages/build/
147 | # Uncomment if necessary however generally it will be regenerated when needed
148 | #!**/packages/repositories.config
149 |
150 | # Windows Azure Build Output
151 | csx/
152 | *.build.csdef
153 |
154 | # Windows Store app package directory
155 | AppPackages/
156 |
157 | # Others
158 | *.[Cc]ache
159 | ClientBin/
160 | [Ss]tyle[Cc]op.*
161 | ~$*
162 | *~
163 | *.dbmdl
164 | *.dbproj.schemaview
165 | *.pfx
166 | *.publishsettings
167 | node_modules/
168 | bower_components/
169 |
170 | # RIA/Silverlight projects
171 | Generated_Code/
172 |
173 | # Backup & report files from converting an old project file
174 | # to a newer Visual Studio version. Backup files are not needed,
175 | # because we have git ;-)
176 | _UpgradeReport_Files/
177 | Backup*/
178 | UpgradeLog*.XML
179 | UpgradeLog*.htm
180 |
181 | # SQL Server files
182 | *.mdf
183 | *.ldf
184 |
185 | # Business Intelligence projects
186 | *.rdl.data
187 | *.bim.layout
188 | *.bim_*.settings
189 |
190 | # Microsoft Fakes
191 | FakesAssemblies/
192 |
193 | # Node.js Tools for Visual Studio
194 | .ntvs_analysis.dat
195 |
196 | # Visual Studio 6 build log
197 | *.plg
198 |
199 | # Visual Studio 6 workspace options file
200 | *.opt
201 |
202 | *.v2.ncrunchsolution
203 |
--------------------------------------------------------------------------------
/.paket/Paket.Restore.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
8 |
9 | true
10 | $(MSBuildThisFileDirectory)
11 | $(MSBuildThisFileDirectory)..\
12 | $(PaketRootPath)paket-files\paket.restore.cached
13 | $(PaketRootPath)paket.lock
14 | /Library/Frameworks/Mono.framework/Commands/mono
15 | mono
16 |
17 | $(PaketRootPath)paket.exe
18 | $(PaketToolsPath)paket.exe
19 | "$(PaketExePath)"
20 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)"
21 |
22 |
23 | <_PaketExeExtension>$([System.IO.Path]::GetExtension("$(PaketExePath)"))
24 | dotnet "$(PaketExePath)"
25 |
26 |
27 | "$(PaketExePath)"
28 |
29 | $(PaketRootPath)paket.bootstrapper.exe
30 | $(PaketToolsPath)paket.bootstrapper.exe
31 | "$(PaketBootStrapperExePath)"
32 | $(MonoPath) --runtime=v4.0.30319 "$(PaketBootStrapperExePath)"
33 |
34 |
35 |
36 |
37 | true
38 | true
39 |
40 |
41 |
42 |
43 |
44 |
45 | true
46 | $(NoWarn);NU1603
47 |
48 |
49 |
50 |
51 | /usr/bin/shasum $(PaketRestoreCacheFile) | /usr/bin/awk '{ print $1 }'
52 | /usr/bin/shasum $(PaketLockFilePath) | /usr/bin/awk '{ print $1 }'
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | $([System.IO.File]::ReadAllText('$(PaketRestoreCacheFile)'))
66 | $([System.IO.File]::ReadAllText('$(PaketLockFilePath)'))
67 | true
68 | false
69 | true
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | $(MSBuildProjectDirectory)\obj\$(MSBuildProjectFile).paket.references.cached
79 |
80 | $(MSBuildProjectFullPath).paket.references
81 |
82 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references
83 |
84 | $(MSBuildProjectDirectory)\paket.references
85 | $(MSBuildProjectDirectory)\obj\$(MSBuildProjectFile).$(TargetFramework).paket.resolved
86 | true
87 | references-file-or-cache-not-found
88 |
89 |
90 |
91 |
92 | $([System.IO.File]::ReadAllText('$(PaketReferencesCachedFilePath)'))
93 | $([System.IO.File]::ReadAllText('$(PaketOriginalReferencesFilePath)'))
94 | references-file
95 | false
96 |
97 |
98 |
99 |
100 | false
101 |
102 |
103 |
104 |
105 | true
106 | target-framework '$(TargetFramework)'
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0])
124 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1])
125 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4])
126 |
127 |
128 | %(PaketReferencesFileLinesInfo.PackageVersion)
129 | All
130 |
131 |
132 |
133 |
134 | $(MSBuildProjectDirectory)/obj/$(MSBuildProjectFile).paket.clitools
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[0])
144 | $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[1])
145 |
146 |
147 | %(PaketCliToolFileLinesInfo.PackageVersion)
148 |
149 |
150 |
151 |
155 |
156 |
157 |
158 |
159 |
160 | false
161 |
162 |
163 |
164 |
165 |
166 | <_NuspecFilesNewLocation Include="$(BaseIntermediateOutputPath)$(Configuration)\*.nuspec"/>
167 |
168 |
169 |
170 | $(MSBuildProjectDirectory)/$(MSBuildProjectFile)
171 | true
172 | false
173 | true
174 | $(BaseIntermediateOutputPath)$(Configuration)
175 | $(BaseIntermediateOutputPath)
176 |
177 |
178 |
179 | <_NuspecFiles Include="$(AdjustedNuspecOutputPath)\*.nuspec"/>
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
232 |
233 |
274 |
275 |
276 |
277 |
--------------------------------------------------------------------------------
/.paket/paket.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hoonzis/fluentnest/92c6b01aac12c8ced260aa847bd4908b795fb56e/.paket/paket.exe
--------------------------------------------------------------------------------
/.paket/paket.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 |
7 | true
8 | $(MSBuildThisFileDirectory)
9 | $(MSBuildThisFileDirectory)..\
10 | /Library/Frameworks/Mono.framework/Commands/mono
11 | mono
12 |
13 |
14 |
15 | $(PaketToolsPath)paket.exe
16 | $(PaketToolsPath)paket.bootstrapper.exe
17 | "$(PaketExePath)"
18 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)"
19 | "$(PaketBootStrapperExePath)" $(PaketBootStrapperCommandArgs)
20 | $(MonoPath) --runtime=v4.0.30319 $(PaketBootStrapperExePath) $(PaketBootStrapperCommandArgs)
21 |
22 | $(MSBuildProjectDirectory)\paket.references
23 | $(MSBuildStartupDirectory)\paket.references
24 | $(MSBuildProjectFullPath).paket.references
25 | $(PaketCommand) restore --references-files "$(PaketReferences)"
26 | $(PaketBootStrapperCommand)
27 |
28 | RestorePackages; $(BuildDependsOn);
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Directory.build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | FluentNest
4 | Jan Fajfr
5 | Fluent query language for ElasticSearch
6 | https://github.com/hoonzis/fluentnest
7 | Copyright © Jan Fajfr 2015
8 | ElasticSearch search NEST
9 | 1.0.0.0
10 | 1.0.0.0
11 |
12 |
--------------------------------------------------------------------------------
/FluentNest.Tests/.gitignore:
--------------------------------------------------------------------------------
1 | *.v2.ncrunchproject
2 |
--------------------------------------------------------------------------------
/FluentNest.Tests/Benchmarking.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using FluentNest.Tests.Model;
5 | using Nest;
6 | using NFluent;
7 | using Tests;
8 | using Xunit;
9 |
10 | namespace FluentNest.Tests
11 | {
12 | public class Benchmarking : TestsBase
13 | {
14 | private string AddSimpleTestData()
15 | {
16 | var indexName = "index_" + Guid.NewGuid();
17 |
18 | Client.CreateIndex(indexName, x => x.Mappings(m => m
19 | .Map(t => t
20 | .Properties(prop => prop.Keyword(str => str.Name(s => s.Guid)))
21 | .Properties(prop => prop.Keyword(str => str.Name(s => s.Email)))
22 | )));
23 |
24 | var cars = new List();
25 | for (int i = 0; i < 10000; i++)
26 | {
27 | var car = new Car
28 | {
29 | Id = Guid.NewGuid(),
30 | Timestamp = new DateTime(2010, (i % 12) + 1, 1),
31 | Name = "name" + i % 3,
32 | Price = 10,
33 | Sold = i % 2 == 0,
34 | CarType = "Type" + i % 2,
35 | Emissions = i + 1,
36 | Guid = "test-" + i,
37 | Email = "Email@email" + i % 2 + ".com",
38 | Age = i + 1,
39 | Enabled = i % 2 == 0,
40 | Active = i % 2 == 0,
41 | EngineType = i % 2 == 0 ? EngineType.Diesel : EngineType.Standard
42 | };
43 | cars.Add(car);
44 | }
45 | Client.Bulk(x => x.CreateMany(cars).Index(indexName));
46 | Client.Flush(indexName);
47 | return indexName;
48 | }
49 |
50 | // This should confirm the theory that a two side range is faster then a bool with 2-one sided ranges
51 | [Fact]
52 | public void WithMergedRange()
53 | {
54 | var stopWatch = new Stopwatch();
55 | var index = AddSimpleTestData();
56 | var sc = new SearchDescriptor().Index(index).FilterOn(x => x.Emissions > 2 && x.Emissions < 6 && x.Price < 20);
57 | var json = Serialize(sc);
58 | Console.WriteLine(json);
59 |
60 | stopWatch.Start();
61 | var allCars = Client.Search(sc);
62 | stopWatch.Stop();
63 |
64 | Console.WriteLine("Query time:" + stopWatch.Elapsed);
65 | Check.That(allCars.Documents).HasSize(3);
66 | }
67 |
68 | [Fact]
69 | public void WithoutMergedRange()
70 | {
71 | var stopWatch = new Stopwatch();
72 | var index = AddSimpleTestData();
73 | var sc = new SearchDescriptor().Index(index).FilterOn(x => x.Emissions > 2 && x.Price < 20 && x.Emissions < 6);
74 | var json = Serialize(sc);
75 | Console.WriteLine(json);
76 |
77 | stopWatch.Start();
78 | var allCars2 = Client.Search(sc);
79 | stopWatch.Stop();
80 |
81 | Console.WriteLine("Query time:" + stopWatch.Elapsed);
82 | Check.That(allCars2.Documents).HasSize(3);
83 | }
84 |
85 | [Fact]
86 | public void WithMergedAndFilters()
87 | {
88 | var stopWatch = new Stopwatch();
89 | var index = AddSimpleTestData();
90 | var sc =
91 | new SearchDescriptor().Index(index).FilterOn(
92 | x =>
93 | x.Emissions < 6 && x.Sold == true && x.Price > 4 && x.EngineType == EngineType.Diesel &&
94 | x.Length < 4);
95 |
96 | var json = Serialize(sc);
97 | Console.WriteLine(json);
98 |
99 | stopWatch.Start();
100 | var cars = Client.Search(sc);
101 | stopWatch.Stop();
102 |
103 | Console.WriteLine("Query time:" + stopWatch.Elapsed);
104 | Check.That(cars.Documents).HasSize(3);
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/FluentNest.Tests/DeleteTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using FluentNest.Tests.Model;
3 | using Nest;
4 | using NFluent;
5 | using Tests;
6 | using Xunit;
7 |
8 | namespace FluentNest.Tests
9 | {
10 | public class DeleteTests : TestsBase
11 | {
12 | public string AddSimpleTestData()
13 | {
14 | var indexName = "index_" + Guid.NewGuid();
15 | Client.CreateIndex(indexName, x => x.Mappings(
16 | m => m.Map(t => t
17 | .Properties(prop => prop.Keyword(str => str.Name(s => s.EngineType))))));
18 |
19 | for (int i = 0; i < 10; i++)
20 | {
21 | var car = new Car
22 | {
23 | Id = Guid.NewGuid(),
24 | Timestamp = new DateTime(2010, i + 1, 1),
25 | Sold = i % 2 == 0,
26 | CarType = "Type" + i % 3,
27 | Length = i,
28 | EngineType = i % 2 == 0 ? EngineType.Diesel : EngineType.Standard,
29 | Weight = 5
30 | };
31 |
32 | Client.Index(car, ind => ind.Index(indexName));
33 | }
34 | Client.Flush(indexName);
35 | return indexName;
36 | }
37 |
38 | [Fact]
39 | public void DeleteByQuery()
40 | {
41 | var index = AddSimpleTestData();
42 | var deleteResult = Client.DeleteByQuery(s => s.FilterOn(x => x.Sold).Index(index).Type(Types.AllTypes));
43 | Check.That(deleteResult.IsValid).IsTrue();
44 | Client.Refresh(index);
45 | var result = Client.Search(sc=>sc.Index(index).MatchAll());
46 | Check.That(result.Hits).HasSize(5);
47 | Client.DeleteIndex(index);
48 | }
49 |
50 | [Fact]
51 | public void DeleteByQuery_FilterCreatedSeparately()
52 | {
53 | var index = AddSimpleTestData();
54 | var filter = Filters.CreateFilter(x => x.EngineType == EngineType.Diesel);
55 | var deleteResult = Client.DeleteByQuery(s => s.FilterOn(filter).Index(index).Type(Types.AllTypes));
56 | Check.That(deleteResult.IsValid).IsTrue();
57 | Client.Refresh(index);
58 | var result = Client.Search(sc => sc.Index(index).MatchAll());
59 | Check.That(result.Hits).HasSize(5);
60 | Client.DeleteIndex(index);
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/FluentNest.Tests/EnumTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Elasticsearch.Net;
5 | using FluentNest.Tests.Model;
6 | using Nest;
7 | using Nest.JsonNetSerializer;
8 | using Newtonsoft.Json;
9 | using Newtonsoft.Json.Converters;
10 | using NFluent;
11 | using Tests;
12 | using Xunit;
13 |
14 | namespace FluentNest.Tests
15 | {
16 | public class EnumTests : TestsBase
17 | {
18 | private class StringEnumContractSerializer : ConnectionSettingsAwareSerializerBase
19 | {
20 | public StringEnumContractSerializer(IElasticsearchSerializer builtinSerializer, IConnectionSettingsValues connectionSettings)
21 | : base(builtinSerializer, connectionSettings)
22 | {
23 | }
24 |
25 | protected override IEnumerable CreateJsonConverters()
26 | {
27 | return base.CreateJsonConverters().Concat(new[] {new StringEnumConverter()});
28 | }
29 | }
30 |
31 | public EnumTests()
32 | : base((builtIn, values) => new StringEnumContractSerializer(builtIn, values))
33 | {
34 |
35 | }
36 |
37 | public string AddSimpleTestData()
38 | {
39 | var indexName = "index_" + Guid.NewGuid();
40 | Client.CreateIndex(indexName, x => x.Mappings(
41 | m => m.Map(t => t
42 | .Properties(prop => prop
43 | .Keyword(str => str.Name(s => s.EngineType))
44 | .Keyword(str => str.Name(s => s.NullableEngineType))))));
45 |
46 | for (int i = 0; i < 10; i++)
47 | {
48 | var car = new Car
49 | {
50 | Id = Guid.NewGuid(),
51 | Timestamp = new DateTime(2010, i + 1, 1),
52 | Name = "Car" + i,
53 | Price = 10,
54 | Sold = i % 2 == 0,
55 | CarType = "Type" + i % 3,
56 | Length = i,
57 | EngineType = i % 2 == 0 ? EngineType.Diesel : EngineType.Standard,
58 | NullableEngineType = i % 2 == 0 ? EngineType.Diesel : EngineType.Standard,
59 | Weight = 5,
60 | ConditionalRanking = i % 2 == 0 ? null : (int?)i,
61 | Description = "Desc" + i,
62 | };
63 | Client.Index(car, ind => ind.Index(indexName));
64 | }
65 | Client.Flush(indexName);
66 | return indexName;
67 | }
68 |
69 | [Fact]
70 | public void Filtering_on_enum_property_should_work()
71 | {
72 | var index = AddSimpleTestData();
73 | var result = Client.Search(s => s.Index(index).FilterOn(x => x.EngineType == EngineType.Diesel));
74 | Check.That(result.Hits.Count()).IsEqualTo(5);
75 | Client.DeleteIndex(index);
76 | }
77 |
78 | [Fact]
79 | public void Filtering_on_nullable_enum_property_should_work()
80 | {
81 | var index = AddSimpleTestData();
82 | var result = Client.Search(s => s.Index(index).FilterOn(x => x.NullableEngineType == EngineType.Diesel));
83 | Check.That(result.Hits.Count()).IsEqualTo(5);
84 | Client.DeleteIndex(index);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/FluentNest.Tests/FilterTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using FluentNest.Tests.Model;
5 | using Nest;
6 | using NFluent;
7 | using Tests;
8 | using Xunit;
9 |
10 | namespace FluentNest.Tests
11 | {
12 | public class FilterTests : TestsBase
13 | {
14 | private const string MyFavoriteGuid = "test-test";
15 |
16 | private string AddSimpleTestData()
17 | {
18 | var indexName = "index_" + Guid.NewGuid();
19 |
20 | Client.CreateIndex(indexName, x => x.Mappings(m => m
21 | .Map(t => t
22 | .Properties(prop => prop.Keyword(str => str.Name(s => s.Guid)))
23 | .Properties(prop => prop.Keyword(str => str.Name(s => s.Email)))
24 | .Properties(prop => prop.Keyword(str => str.Name(s => s.PreviousOwners)))
25 | )));
26 |
27 | var cars = new List();
28 | for (int i = 0; i < 10; i++)
29 | {
30 | var car = new Car
31 | {
32 | Id = Guid.NewGuid(),
33 | Timestamp = new DateTime(2010,(i%12)+1,1),
34 | Name = "name"+i%3,
35 | Price = 10,
36 | Sold = i % 2 == 0,
37 | CarType = "Type" + i%2,
38 | Emissions = i+1,
39 | Guid = "test-" + i,
40 | Email = "Email@email" + i % 2 + ".com",
41 | Age = i + 1,
42 | Enabled = i % 2 == 0,
43 | Active = i % 2 == 0,
44 | Weight = i % 3 == 0 ? 10m : (decimal?)null,
45 | PreviousOwners = i % 2 == 0 ? null : i % 3 == 0 ? new string[0] : Enumerable.Range(0, i).Select(n => $"Owner n°{n}").ToArray()
46 | };
47 | if (i == 1)
48 | {
49 | car.Guid = MyFavoriteGuid;
50 | }
51 | cars.Add(car);
52 | }
53 | Client.Bulk(x => x.CreateMany(cars).Index(indexName));
54 | Client.Flush(indexName);
55 | return indexName;
56 | }
57 |
58 | [Fact]
59 | public void DateComparisonAndTerm()
60 | {
61 | var index = AddSimpleTestData();
62 |
63 | var startDate = new DateTime(2010, 1, 1);
64 | var endDate = new DateTime(2010, 3, 1);
65 | var result = Client.Search(s => s.Index(index).FilterOn(x => x.Timestamp >= startDate && x.Timestamp <= endDate && x.CarType == "type0"));
66 | Client.DeleteIndex(index);
67 |
68 | Check.That(result.Documents).HasSize(2);
69 | }
70 |
71 |
72 | [Fact]
73 | public void NullableDateComparisonAndTerm()
74 | {
75 | var index = AddSimpleTestData();
76 |
77 | DateTime? startDate = null;
78 | var endDate = new DateTime(2010, 3, 1);
79 | var result = Client.Search(s => s.Index(index).FilterOn(x => x.Timestamp >= startDate && x.Timestamp <= endDate && x.CarType == "type0"));
80 | Client.DeleteIndex(index);
81 |
82 | Check.That(result.Documents).HasSize(2);
83 | }
84 |
85 | [Fact]
86 | public void DateEqualityTest()
87 | {
88 | var index = AddSimpleTestData();
89 |
90 | var startDate = new DateTime(2010, 1, 1);
91 | var result = Client.Search(s => s.Index(index).FilterOn(x => x.Timestamp == startDate));
92 | Client.DeleteIndex(index);
93 | Check.That(result.Documents).HasSize(1);
94 | }
95 |
96 | [Fact]
97 | public void TestEqualityFilter()
98 | {
99 | var index = AddSimpleTestData();
100 |
101 | var carType = "Type0".ToLower();
102 | // Standard Nest way of getting the documents. Values are lowered by ES
103 | var result = Client.Search(s => s.Index(index).Query(x => x.Term(f => f.CarType, carType)));
104 | Check.That(result.Documents).HasSize(5);
105 |
106 | // Best way
107 | result = Client.Search(s => s.Index(index).FilterOn(x => x.CarType == carType));
108 | Check.That(result.Documents).HasSize(5);
109 | Client.DeleteIndex(index);
110 | }
111 |
112 | [Fact]
113 | public void TestRangeFilters_And_And()
114 | {
115 | var index = AddSimpleTestData();
116 |
117 | var startDate = new DateTime(2010, 1, 1);
118 | var endDate = new DateTime(2010, 5, 1);
119 |
120 | var result = Client.Search(s => s.Index(index).Query(
121 | q => q.Bool(b => b.Must(left => left.DateRange(f => f.Field(fd => fd.Timestamp).GreaterThan(startDate)),
122 | right => right.DateRange(f => f.Field(fd => fd.Timestamp).LessThan(endDate)))
123 | )
124 | ));
125 | Check.That(result.Documents).HasSize(3);
126 |
127 | //Much better
128 | result = Client.Search(s => s.Index(index).FilterOn(f => f.Timestamp > startDate && f.Timestamp < endDate));
129 | Check.That(result.Documents).HasSize(3);
130 | Client.DeleteIndex(index);
131 | }
132 |
133 | [Fact]
134 | public void FilterOnSpecialCharacter()
135 | {
136 | var index = AddSimpleTestData();
137 |
138 | // these two searches should provide the same result
139 | var result =
140 | Client.Search(
141 | s =>
142 | s.Index(index)
143 | .Query(
144 | q =>
145 | q.Bool(
146 | b => b.Must(x => x.Term(term => term.Email, "Email@email1.com")))));
147 | Check.That(result.Documents).HasSize(5);
148 |
149 | result = Client.Search(s => s.Index(index).FilterOn(f => f.Email == "Email@email1.com"));
150 | Check.That(result.Documents).HasSize(5);
151 | Client.DeleteIndex(index);
152 | }
153 |
154 | [Fact]
155 | public void TestConsecutiveFilters()
156 | {
157 | var index = AddSimpleTestData();
158 |
159 | var filter = Filters
160 | .CreateFilter(x => x.Name == "name1" && x.Age >= 5)
161 | .AndFilteredOn(x => x.Email == "Email@email1.com");
162 |
163 | var cars = Client.Search(s => s.Index(index).Query(_ => filter));
164 | Client.DeleteIndex(index);
165 | Check.That(cars.Documents).HasSize(1);
166 | }
167 |
168 | [Fact]
169 | public void TestConsecutiveFiltersOnBoolean()
170 | {
171 | var index = AddSimpleTestData();
172 |
173 | var filter = Filters
174 | .CreateFilter(x => x.Name == "name1" && x.Age >= 5)
175 | .AndFilteredOn(x => x.Active);
176 |
177 | var cars = Client.Search(s => s.Index(index).Query(_ => filter));
178 | Client.DeleteIndex(index);
179 | Check.That(cars.Documents).HasSize(1);
180 | }
181 |
182 | [Fact]
183 | public void MultipleFiltersAndSomeAggregations()
184 | {
185 | var index = AddSimpleTestData();
186 |
187 | var filter = Filters
188 | .CreateFilter(x => x.Name == "name1" && x.Age >= 5)
189 | .AndFilteredOn(x => x.Email == "Email@email1.com");
190 |
191 | var result = Client.Search(sc => sc
192 | .Index(index)
193 | .FilterOn(filter)
194 | .Aggregations(agg => agg
195 | .SumBy(x=>x.Age)
196 | ));
197 |
198 | var sumValue = result.Aggregations.GetSum(x => x.Age);
199 |
200 | var aggsContainer = result.Aggregations.AsContainer();
201 | var sum2 = aggsContainer.GetSum(x => x.Age);
202 | Client.DeleteIndex(index);
203 | Check.That(sumValue).Equals(8);
204 | Check.That(sum2).Equals(8);
205 | }
206 |
207 | [Fact]
208 | public void Or_Filter_Test()
209 | {
210 | var index = AddSimpleTestData();
211 | var cars = Client.Search(s => s.Index(index).FilterOn(x=> x.Name == "name1" || x.Age >= 5));
212 | Client.DeleteIndex(index);
213 | Check.That(cars.Documents).HasSize(7);
214 | }
215 |
216 | [Fact]
217 | public void Noq_equal_Filter_Test()
218 | {
219 | var index = AddSimpleTestData();
220 |
221 | var filter = Filters
222 | .CreateFilter(x => x.Name != "name1" && x.Name != "name2");
223 |
224 | var allCars = Client.Search(s => s.Index(index).Query(_ => filter));
225 | Check.That(allCars.Documents).HasSize(4);
226 | Client.DeleteIndex(index);
227 | }
228 |
229 | [Fact]
230 | public void Bool_filter_test()
231 | {
232 | var index = AddSimpleTestData();
233 | var allCars = Client.Search(s=>s.Index(index).FilterOn(f=>f.Active));
234 | Check.That(allCars.Documents).HasSize(5);
235 |
236 | var filter = Filters.CreateFilter(x => x.Enabled);
237 |
238 | var allCars2 = Client.Search(s => s.Index(index).Query(_ => filter));
239 | Check.That(allCars2.Documents).HasSize(5);
240 | Client.DeleteIndex(index);
241 | }
242 |
243 | [Fact]
244 | public void Null_filter_test()
245 | {
246 | var index = AddSimpleTestData();
247 | var allCars = Client.Search(sd => sd.Index(index).Query(x => x.Bool(b => b.MustNot(n => n.Exists(c => c.Field(s => s.Weight))))));
248 | Check.That(allCars.Documents).HasSize(6);
249 |
250 | var filter = Filters.CreateFilter(x => x.Weight == null);
251 |
252 | var weightlessCars = Client.Search(s => s.Index(index).Query(_ => filter));
253 | Check.That(weightlessCars.Documents).HasSize(6);
254 | Client.DeleteIndex(index);
255 | }
256 |
257 | [Fact]
258 | public void NotNull_filter_test()
259 | {
260 | var index = AddSimpleTestData();
261 | var allCars = Client.Search(sd => sd.Index(index).Query(x => x.Bool(b => b.Must(n => n.Exists(c => c.Field(s => s.Weight))))));
262 | Check.That(allCars.Documents).HasSize(4);
263 |
264 | var filter = Filters.CreateFilter(x => x.Weight != null);
265 |
266 | var weightlessCars = Client.Search(s => s.Index(index).Query(_ => filter));
267 | Check.That(weightlessCars.Documents).HasSize(4);
268 | Client.DeleteIndex(index);
269 | }
270 |
271 | [Fact]
272 | public void Decimal_Two_Side_Range_Test()
273 | {
274 | var index = AddSimpleTestData();
275 | var allCars = Client.Search(s => s.Index(index).FilterOn(x=>x.Emissions > 2 && x.Emissions < 6));
276 | Client.DeleteIndex(index);
277 | Check.That(allCars.Documents).HasSize(3);
278 | }
279 |
280 | [Fact]
281 | public void Guid_Filter_Test()
282 | {
283 | var index = AddSimpleTestData();
284 | var result = Client.Search(sc => sc.Index(index).FilterOn(x => x.Guid == MyFavoriteGuid));
285 | Check.That(result.Documents).HasSize(1);
286 | Client.DeleteIndex(index);
287 | }
288 |
289 | [Fact]
290 | public void Filter_ValueWithin_SingleItem()
291 | {
292 | var item = "Owner n°0";
293 | var index = AddSimpleTestData();
294 | var result = Client.Search(sc => sc.Index(index).FilterOn(Filters.ValueWithin(x => x.PreviousOwners, item)).TypedKeys(null));
295 | Check.That(result.Documents).Not.HasSize(0);
296 | foreach (var previousOwners in result.Documents.Select(d => d.PreviousOwners))
297 | {
298 | Check.That(previousOwners).Contains(item);
299 | }
300 |
301 | Client.DeleteIndex(index);
302 | }
303 |
304 | [Fact]
305 | public void Filter_ValueWithin_MultipleItems()
306 | {
307 | var items = new[] { "Owner n°0", "Onwer n°1" };
308 | var index = AddSimpleTestData();
309 | var result = Client.Search(sc => sc.Index(index).FilterOn(Filters.ValueWithin(x => x.PreviousOwners, items)).TypedKeys(null));
310 | Check.That(result.Documents).Not.HasSize(0);
311 | foreach (var previousOwners in result.Documents.Select(d => d.PreviousOwners))
312 | {
313 | Check.That(previousOwners.Join(items, a => a, b => b, (a, b) => 0)).Not.HasSize(0);
314 | }
315 |
316 | Client.DeleteIndex(index);
317 | }
318 |
319 | [Fact]
320 | public void Integer_Two_Side_Range_Test()
321 | {
322 | var sc = new SearchDescriptor().FilterOn(x => x.Age > 2 && x.Age < 6);
323 | CheckSD(sc, "Integer_Two_Side_Range_Test");
324 | }
325 |
326 | [Fact]
327 | public void Three_Ands_Test()
328 | {
329 | var sc = new SearchDescriptor().FilterOn(x => x.Sold && x.Age < 6 && x.Emissions < 5);
330 | CheckSD(sc, "Three_Ands_Test");
331 | }
332 |
333 | [Fact]
334 | public void Filter_ValueWithin_Test()
335 | {
336 | var list = new List {"name1", "name2"};
337 | var sc = new SearchDescriptor().FilterOn(Filters.ValueWithin(x => x.Name, list));
338 | CheckSD(sc, "Filter_ValueWithin_Test");
339 | }
340 |
341 | [Fact]
342 | public void Filter_ValueWithin_AddedOnExistingFilter()
343 | {
344 | var filter = Filters.CreateFilter(x => x.Age > 8);
345 | var sc = new SearchDescriptor().FilterOn(filter.AndValueWithin(x=>x.Name, new List { "name1", "name2" } ));
346 | CheckSD(sc, "Filter_ValueWithin_AddedOnExistingFilter");
347 | }
348 |
349 | [Fact]
350 | public void Custom_Field_Name_Test()
351 | {
352 | var sc = new SearchDescriptor().FilterOn(x => x.GetFieldNamed("sold") && x.GetFieldNamed("age") < 6 && x.GetFieldNamed("emissions") < 5);
353 | CheckSD(sc, "Three_Ands_Test");
354 | }
355 |
356 | [Fact]
357 | public void Concatenated_custom_Field_Name_Test()
358 | {
359 | var old = "old";
360 |
361 | DoTest(old);
362 |
363 | void DoTest(string o)
364 | {
365 | var sc = new SearchDescriptor().FilterOn(x => x.GetFieldNamed("s" + o) && x.GetFieldNamed("age") < 6 && x.GetFieldNamed("emissions") < 5);
366 | CheckSD(sc, "Three_Ands_Test");
367 | }
368 | }
369 | }
370 | }
371 |
372 |
--------------------------------------------------------------------------------
/FluentNest.Tests/FilterTests.txt:
--------------------------------------------------------------------------------
1 | ###Three_Ands_Test***
2 | {
3 | "query": {
4 | "bool": {
5 | "must": [
6 | {
7 | "term": {
8 | "sold": {
9 | "value": true
10 | }
11 | }
12 | },
13 | {
14 | "range": {
15 | "age": {
16 | "lt": 6.0
17 | }
18 | }
19 | },
20 | {
21 | "range": {
22 | "emissions": {
23 | "lt": 5.0
24 | }
25 | }
26 | }
27 | ]
28 | }
29 | }
30 | }
31 | ###Integer_Two_Side_Range_Test***
32 | {
33 | "query": {
34 | "range": {
35 | "age": {
36 | "gt": 2.0,
37 | "lt": 6.0
38 | }
39 | }
40 | }
41 | }
42 | ###Filter_ValueWithin_Test***
43 | {
44 | "query": {
45 | "bool": {
46 | "must": [
47 | {
48 | "terms": {
49 | "name": [
50 | "name1",
51 | "name2"
52 | ]
53 | }
54 | }
55 | ]
56 | }
57 | }
58 | }
59 | ###Filter_ValueWithin_AddedOnExistingFilter***
60 | {
61 | "query": {
62 | "bool": {
63 | "must": [
64 | {
65 | "range": {
66 | "age": {
67 | "gt": 8.0
68 | }
69 | }
70 | },
71 | {
72 | "terms": {
73 | "name": [
74 | "name1",
75 | "name2"
76 | ]
77 | }
78 | }
79 | ]
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/FluentNest.Tests/FluentNest.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | false
6 | true
7 |
8 |
9 |
10 | Always
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/FluentNest.Tests/GroupByTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using FluentNest.Tests.Model;
5 | using NFluent;
6 | using Tests;
7 | using Xunit;
8 | using User = FluentNest.Tests.Model.User;
9 |
10 | namespace FluentNest.Tests
11 | {
12 | public class GroupByTests : TestsBase
13 | {
14 | public string CreateTestIndex()
15 | {
16 | var indexName = "index_" + Guid.NewGuid();
17 | Client.CreateIndex(indexName, x => x.Mappings(
18 | m => m.Map(t => t
19 | .Properties(prop => prop.Keyword(str => str.Name(s => s.EngineType)))
20 | .Properties(prop => prop.Text(str => str.Name(s => s.CarType).Fielddata()))
21 | )));
22 |
23 | return indexName;
24 | }
25 |
26 | public string AddSimpleTestData()
27 | {
28 | var indexName = CreateTestIndex();
29 |
30 | for (int i = 0; i < 10; i++)
31 | {
32 | var car = new Car
33 | {
34 | Id = Guid.NewGuid(),
35 | Timestamp = new DateTime(2010, i + 1, 1),
36 | Name = "Car" + i,
37 | Price = 10,
38 | Sold = i % 2 == 0,
39 | CarType = "Type" + i % 3,
40 | Length = i,
41 | EngineType = i % 2 == 0 ? EngineType.Diesel : EngineType.Standard,
42 | Weight = 5,
43 | ConditionalRanking = i % 2 == 0 ? null : (int?)i,
44 | Description = "Desc" + i,
45 | };
46 | Client.Index(car, ind => ind.Index(indexName));
47 | }
48 | Client.Flush(indexName);
49 | return indexName;
50 | }
51 |
52 | [Fact]
53 | public void NestedGroupBy()
54 | {
55 | var index = AddSimpleTestData();
56 |
57 | // The standard NEST way without FluentNest
58 | var result = Client.Search(s => s.Index(index)
59 | .Aggregations(fstAgg => fstAgg
60 | .Terms("firstLevel", f => f
61 | .Field(z => z.CarType)
62 | .Aggregations(sndLevel => sndLevel
63 | .Terms("secondLevel", f2 => f2.Field(f3 => f3.EngineType)
64 | .Aggregations(sums => sums
65 | .Sum("priceSum", son => son
66 | .Field(f4 => f4.Price))
67 | )
68 | )
69 | )
70 | )
71 | )
72 | );
73 |
74 | var carTypesAgg = result.Aggregations.Terms("firstLevel");
75 |
76 | foreach (var carType in carTypesAgg.Buckets)
77 | {
78 | var engineTypes = carType.Terms("secondLevel");
79 | foreach (var engineType in engineTypes.Buckets)
80 | {
81 | var priceSum = (decimal)engineType.Sum("priceSum").Value;
82 | Check.That(priceSum).IsPositive();
83 | }
84 | }
85 |
86 | // Now with FluentNest
87 | result =Client.Search(search => search.Index(index).Aggregations(agg => agg
88 | .SumBy(s => s.Price)
89 | .GroupBy(s => s.EngineType)
90 | .GroupBy(b => b.CarType)
91 | ));
92 |
93 | var aggsContainer = result.Aggregations.AsContainer();
94 | var carTypes = aggsContainer.GetGroupBy(x => x.CarType).ToList();
95 | Check.That(carTypes).HasSize(3);
96 | foreach (var carType in carTypes)
97 | {
98 | var container = carType.AsContainer();
99 | var engineTypes = container.GetGroupBy(x => x.EngineType, k => new CarType
100 | {
101 | Type = k.Key,
102 | Price = k.GetSum(x => x.Price)
103 | }).ToList();
104 |
105 | Check.That(engineTypes).HasSize(2);
106 | Check.That(engineTypes.First().Price).Equals(20m);
107 | }
108 | Client.DeleteIndex(index);
109 | }
110 |
111 | [Fact]
112 | public void GetDictionaryFromGroupBy()
113 | {
114 | var index = AddSimpleTestData();
115 |
116 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
117 | .SumBy(s => s.Price)
118 | .GroupBy(s => s.EngineType))
119 | );
120 |
121 | var aggsContainer = result.Aggregations.AsContainer();
122 |
123 | var carTypesList = result.Aggregations.GetGroupBy(x => x.EngineType);
124 | var carTypesDictionary = aggsContainer.GetDictionary(x => x.EngineType);
125 |
126 | Check.That(carTypesDictionary).HasSize(2);
127 | Check.That(carTypesList).HasSize(2);
128 | Check.That(carTypesDictionary.Keys).ContainsExactly(EngineType.Diesel, EngineType.Standard);
129 | Client.DeleteIndex(index);
130 | }
131 |
132 | [Fact]
133 | public void GetDictionaryWithDecimalKeysFromGroupBy()
134 | {
135 | var index = AddSimpleTestData();
136 |
137 | var result =
138 | Client.Search(search => search.Index(index).Aggregations(x => x.GroupBy(s => s.Price)));
139 |
140 | var aggsContainer = result.Aggregations.AsContainer();
141 | var carTypes = aggsContainer.GetDictionary(x => x.Price);
142 | Check.That(carTypes).HasSize(1);
143 | Check.That(carTypes.Keys).ContainsExactly(10m);
144 | Client.DeleteIndex(index);
145 | }
146 |
147 | [Fact]
148 | public void GroupByStringKeys()
149 | {
150 | var index = AddSimpleTestData();
151 |
152 | var result = Client.Search(search => search.Index(index).Aggregations(agg => agg
153 | .SumBy(s => s.Price)
154 | .GroupBy("engineType")
155 | ));
156 |
157 | var engineTypes = result.Aggregations.GetGroupBy("engineType");
158 | Check.That(engineTypes).HasSize(2);
159 | Client.DeleteIndex(index);
160 | }
161 |
162 | [Fact]
163 | public void DynamicGroupByListOfKeys()
164 | {
165 | var index = AddSimpleTestData();
166 |
167 | var result = Client.Search(search => search.Index(index).Aggregations(agg => agg
168 | .SumBy(s => s.Price)
169 | .GroupBy(new List { "engineType", "carType" })
170 | ));
171 |
172 | var engineTypes = result.Aggregations.GetGroupBy("engineType").ToList();
173 | Check.That(engineTypes).HasSize(2);
174 |
175 | foreach (var engineType in engineTypes)
176 | {
177 | var carTypes = engineType.GetGroupBy("carType");
178 | Check.That(carTypes).HasSize(3);
179 | }
180 |
181 | Client.DeleteIndex(index);
182 | }
183 |
184 | [Fact]
185 | public void Distinct_Test()
186 | {
187 | var index = AddSimpleTestData();
188 |
189 | var result = Client.Search(search => search.Index(index).Aggregations(agg => agg
190 | .DistinctBy(x => x.CarType)
191 | .DistinctBy(x => x.EngineType)
192 | ));
193 |
194 | var engineTypes = result.Aggregations.GetDistinct(x => x.EngineType).ToList();
195 |
196 | var container = result.Aggregations.AsContainer();
197 | var distinctCarTypes = container.GetDistinct(x => x.CarType).ToList();
198 |
199 | Check.That(distinctCarTypes).IsNotNull();
200 | Check.That(distinctCarTypes).HasSize(3);
201 | Check.That(distinctCarTypes).ContainsExactly("type0", "type1", "type2");
202 |
203 | Check.That(engineTypes).IsNotNull();
204 | Check.That(engineTypes).HasSize(2);
205 | Check.That(engineTypes).ContainsExactly(EngineType.Diesel, EngineType.Standard);
206 | Client.DeleteIndex(index);
207 | }
208 |
209 | [Fact]
210 | public void Simple_Filtered_Distinct_Test()
211 | {
212 | var index = AddSimpleTestData();
213 |
214 | var result = Client.Search(search => search.Index(index)
215 | .FilterOn(f=> f.CarType == "type0")
216 | .Aggregations(agg => agg
217 | .DistinctBy(x => x.CarType)
218 | .DistinctBy(x => x.EngineType)
219 | )
220 | );
221 |
222 | var distinctCarTypes = result.Aggregations.GetDistinct(x => x.CarType).ToList();
223 | var engineTypes = result.Aggregations.GetDistinct(x => x.EngineType).ToList();
224 |
225 | Check.That(distinctCarTypes).IsNotNull();
226 | Check.That(distinctCarTypes).HasSize(1);
227 | Check.That(distinctCarTypes).ContainsExactly("type0");
228 |
229 | Check.That(engineTypes).IsNotNull();
230 | Check.That(engineTypes).HasSize(2);
231 | Check.That(engineTypes).ContainsExactly(EngineType.Diesel, EngineType.Standard);
232 | Client.DeleteIndex(index);
233 | }
234 |
235 | [Fact]
236 | public void Distinct_Time_And_Term_Filter_Test()
237 | {
238 | var index = AddSimpleTestData();
239 |
240 | var filter = Filters.CreateFilter(x => x.Timestamp > new DateTime(2010,2,1) && x.Timestamp < new DateTime(2010, 8, 1))
241 | .AndFilteredOn(x => x.CarType == "type0");
242 |
243 | var result = Client.Search(sc => sc.Index(index).FilterOn(filter).Aggregations(agg => agg
244 | .DistinctBy(x => x.CarType)
245 | .DistinctBy(x => x.EngineType)
246 | ));
247 |
248 | var distinctCarTypes = result.Aggregations.GetDistinct(x => x.CarType).ToList();
249 | var engineTypes = result.Aggregations.GetDistinct(x => x.EngineType).ToList();
250 |
251 | Check.That(distinctCarTypes).IsNotNull();
252 | Check.That(distinctCarTypes).HasSize(1);
253 | Check.That(distinctCarTypes).ContainsExactly("type0");
254 |
255 | Check.That(engineTypes).IsNotNull();
256 | Check.That(engineTypes).HasSize(2);
257 | Check.That(engineTypes).ContainsExactly(EngineType.Diesel, EngineType.Standard);
258 | Client.DeleteIndex(index);
259 | }
260 |
261 | [Fact]
262 | public void Terms_Aggregation_Big_Size()
263 | {
264 | var index = CreateUsersIndex(200, 20);
265 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg.DistinctBy(x=>x.Nationality)));
266 | var nationalities = result.Aggregations.GetDistinct(x => x.Nationality).ToList();
267 |
268 | Check.That(nationalities).IsNotNull();
269 | Check.That(nationalities).HasSize(20);
270 | }
271 | [Fact]
272 | public void GroupBy_With_TopHits_Specifying_Properties_To_Fetch()
273 | {
274 | var index = AddSimpleTestData();
275 |
276 | var result = Client.Search(search => search.Index(index).Aggregations(agg => agg
277 | //get name and weight for each retrieved document
278 | .TopHits(3, x => x.Name, x => x.Weight)
279 | .GroupBy(b => b.CarType)
280 | ));
281 |
282 |
283 | var carTypes = result.Aggregations.GetGroupBy(x => x.CarType).ToList();
284 | Check.That(carTypes).HasSize(3);
285 | foreach (var carType in carTypes)
286 | {
287 | var hits = carType.GetTopHits().ToList();
288 | Check.That(hits).HasSize(3);
289 | // we have asked only for name and weights
290 | Check.That(hits[0].Name).IsNotNull();
291 | Check.That(hits[0].Weight).IsNotNull();
292 | // description must be null
293 | Check.That(hits[0].Description).IsNull();
294 | }
295 | Client.DeleteIndex(index);
296 | }
297 |
298 | [Fact]
299 | public void GroupBy_With_TopHits_NoProperties_GetsWholeSource()
300 | {
301 | var index = AddSimpleTestData();
302 |
303 | var result = Client.Search(search => search.Index(index).Aggregations(x => x
304 | .TopHits(3)
305 | .GroupBy(b => b.CarType))
306 | );
307 |
308 | var carTypes = result.Aggregations.GetGroupBy(x => x.CarType).ToList();
309 | Check.That(carTypes).HasSize(3);
310 | foreach (var carType in carTypes)
311 | {
312 | var hits = carType.GetTopHits().ToList();
313 | Check.That(hits).HasSize(3);
314 | Check.That(hits[0].Name).IsNotNull();
315 | Check.That(hits[0].Weight).IsNotNull();
316 | Check.That(hits[0].Description).IsNotNull();
317 | }
318 | Client.DeleteIndex(index);
319 | }
320 |
321 | [Fact]
322 | public void TopHits_In_Double_GroupBy()
323 | {
324 | var indexName = CreateUsersIndex(250, 2);
325 |
326 | var result = Client.Search(search => search.Index(indexName).Aggregations(agg => agg
327 | .TopHits(40, x => x.Name)
328 | .GroupBy(b => b.Active)
329 | .GroupBy(b => b.Nationality))
330 | );
331 |
332 | var userByNationality = result.Aggregations.GetGroupBy(x => x.Nationality).ToList();
333 | Check.That(userByNationality).HasSize(2);
334 | foreach (var nationality in userByNationality)
335 | {
336 | var byActive = nationality.GetGroupBy(x => x.Active).ToList();
337 |
338 | var activeUsers = byActive[0];
339 | var inactiveUser = byActive[1];
340 |
341 |
342 | var hits = activeUsers.GetTopHits().ToList();
343 | Check.That(hits).HasSize(40);
344 | Check.That(hits[0].Name).IsNotNull();
345 |
346 | hits = inactiveUser.GetTopHits().ToList();
347 |
348 | Check.That(hits).HasSize(40);
349 | Check.That(hits[0].Name).IsNotNull();
350 | }
351 | }
352 |
353 | private string CreateUsersIndex(int size, int nationalitiesCount)
354 | {
355 | var indexName = "index_" + Guid.NewGuid();
356 | Client.CreateIndex(indexName, x => x.Mappings(
357 | m => m.Map(t => t
358 | .Properties(prop => prop.Text(str => str.Name(s => s.Nationality).Fielddata()))
359 | .Properties(prop => prop.Text(str => str.Name(s => s.Name).Fielddata()))
360 | .Properties(prop => prop.Text(str => str.Name(s => s.Email).Fielddata()))
361 | )));
362 | var users = new List();
363 | for (int i = 0; i < size; i++)
364 | {
365 | var user = new User
366 | {
367 | Id = Guid.NewGuid(),
368 | Name = "User" + i,
369 | Nationality = "Nationality" + i % nationalitiesCount,
370 | Active = i%3 == 0,
371 | Age = (i + 1) % 10,
372 | Email = "user@" + i
373 | };
374 |
375 | users.Add(user);
376 | }
377 | Client.Bulk(x => x.CreateMany(users).Index(indexName));
378 | Client.Flush(indexName);
379 | return indexName;
380 | }
381 |
382 | [Fact]
383 | public void TopHits_Sorted_SettingSize()
384 | {
385 | var index = CreateUsersIndex(100, 10);
386 | var result = Client.Search(search => search.Index(index).Aggregations(agg => agg
387 | // get 40 first users, sort by name. for each user retrieve name and email
388 | .SortedTopHits(40, x => x.Name, SortType.Ascending, x => x.Name, y => y.Email)
389 | .SortedTopHits(40, x => x.Name, SortType.Descending, x => x.Name, y => y.Email)
390 | .SumBy(x => x.Age)
391 | .GroupBy(b => b.Nationality))
392 | );
393 |
394 | var userByNationality = result.Aggregations.GetGroupBy(x => x.Nationality).ToList();
395 | Check.That(userByNationality).HasSize(10);
396 | var firstNationality = userByNationality.Single(x => x.Key == "nationality0");
397 |
398 | var ascendingHits = firstNationality.GetSortedTopHits(x => x.Name, SortType.Ascending).ToList();
399 | Check.That(ascendingHits).HasSize(10);
400 | Check.That(ascendingHits[0].Name).IsNotNull();
401 |
402 | Check.That(firstNationality.GetSum(x => x.Age)).Equals(10);
403 | Check.That(ascendingHits[0].Name).Equals("User0");
404 | Check.That(ascendingHits[1].Name).Equals("User10");
405 | Check.That(ascendingHits[2].Name).Equals("User20");
406 | Check.That(ascendingHits[3].Name).Equals("User30");
407 |
408 | var descendingHits = firstNationality.GetSortedTopHits(x => x.Name, SortType.Descending).ToList();
409 | Check.That(descendingHits).HasSize(10);
410 | Check.That(descendingHits[0].Name).IsNotNull();
411 |
412 | Check.That(descendingHits[0].Name).Equals("User90");
413 | Check.That(descendingHits[1].Name).Equals("User80");
414 | Check.That(descendingHits[2].Name).Equals("User70");
415 | Check.That(descendingHits[3].Name).Equals("User60");
416 | }
417 |
418 | [Fact]
419 | public void TopHits_Sorted_Supports_Named_Fields()
420 | {
421 | var index = CreateUsersIndex(100, 10);
422 | var result = Client.Search(search => search
423 | .Index(index)
424 | .Aggregations(agg => agg
425 | // get 40 first users, sort by name. for each user retrieve name and email
426 | .SortedTopHits(40, x => x.GetFieldNamed("name"), SortType.Ascending, x => x.GetFieldNamed("name"), y => y.Email)
427 | .SumBy(x => x.Age)
428 | .GroupBy(b => b.Nationality))
429 | );
430 |
431 | var userByNationality = result.Aggregations.GetGroupBy(x => x.Nationality).ToList();
432 | Check.That(userByNationality).HasSize(10);
433 | var firstNotionality = userByNationality.Single(x => x.Key == "nationality0");
434 |
435 | var ascendingHits = firstNotionality.GetSortedTopHits(x => x.GetFieldNamed("name"), SortType.Ascending).ToList();
436 | Check.That(ascendingHits).HasSize(10);
437 | Check.That(ascendingHits[0].Name).IsNotNull();
438 | Check.That(ascendingHits[0].Email).IsNotNull();
439 | }
440 |
441 | [Fact]
442 | public void GettingNonExistingGroup_Test()
443 | {
444 | var index = CreateTestIndex();
445 |
446 | var result = Client.Search(search => search.Index(index).Aggregations(agg => agg
447 | .GroupBy(b => b.Emissions)
448 | ));
449 |
450 | var exception = Record.Exception(() => result.Aggregations.GetGroupBy(x => x.CarType));
451 |
452 | Assert.NotNull(exception);
453 | Assert.IsType(exception);
454 | Check.That(exception.Message).Contains("Available aggregations: GroupByEmissions");
455 | }
456 |
457 | [Fact]
458 | public void NoAggregations_On_The_Result_Test()
459 | {
460 | var index = CreateTestIndex();
461 |
462 | // no data - no aggregations on the result
463 | var result = Client.Search(search => search.Index(index).Aggregations(agg => agg));
464 |
465 | var exception = Record.Exception(() => result.Aggregations.GetGroupBy(x => x.EngineType));
466 |
467 | Assert.NotNull(exception);
468 | Assert.IsType(exception);
469 | Check.That(exception.Message).Contains("No aggregations");
470 | }
471 |
472 | }
473 | }
474 |
--------------------------------------------------------------------------------
/FluentNest.Tests/HistogramTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using FluentNest.Tests.Model;
4 | using Nest;
5 | using NFluent;
6 | using Tests;
7 | using Xunit;
8 |
9 | namespace FluentNest.Tests
10 | {
11 | public class HistogramTests : TestsBase
12 | {
13 | private string AddSimpleTestData()
14 | {
15 | var indexName = "index_" + Guid.NewGuid();
16 | Client.CreateIndex(indexName, x =>
17 | x.Mappings(m => m.Map(t => t
18 | .Properties(prop => prop.Keyword(str => str.Name(s => s.EngineType)))
19 | .Properties(prop => prop.Text(str => str.Name(s => s.CarType).Fielddata()))
20 | )));
21 | for (int i = 0; i < 10; i++)
22 | {
23 | var car = new Car
24 | {
25 | Id = Guid.NewGuid(),
26 | Timestamp = new DateTime(2010,i+1,1),
27 | Name = "Car" + i,
28 | Price = 10,
29 | Sold = i % 2 == 0 ? true : false,
30 | CarType = "Type" + i%3,
31 | Length = i*2,
32 | Weight = i
33 | };
34 | Client.Index(car, ind => ind.Index(indexName));
35 | }
36 | Client.Flush(indexName);
37 | return indexName;
38 | }
39 |
40 | [Fact]
41 | public void MonthlyHistogramPerCarType()
42 | {
43 | var index = AddSimpleTestData();
44 |
45 | var histogram = Client.Search(s => s.Index(index).Aggregations(a => a
46 | .DateHistogram(x => x.Timestamp, DateInterval.Month)
47 | .GroupBy(x => x.CarType))
48 | );
49 |
50 | var aggsContainer = histogram.Aggregations.AsContainer();
51 |
52 | var carTypes = aggsContainer.GetDictionary(x => x.CarType, v => v.GetDateHistogram(f => f.Timestamp));
53 |
54 | Check.That(carTypes).HasSize(3);
55 |
56 | // currently nest returns buckets in between the values
57 | // first type gets everything between first month and the 10th month -> that is 10 buckets
58 | var firstType = carTypes["type0"];
59 | Check.That(firstType).HasSize(10);
60 |
61 | // second type and third type both get 7 buckets
62 | // second type from february to september
63 | var secondType = carTypes["type1"];
64 | Check.That(secondType).HasSize(7);
65 |
66 | // third type from marz to october
67 | var thirdType = carTypes["type2"];
68 | Check.That(thirdType).HasSize(7);
69 |
70 | Client.DeleteIndex(index);
71 | }
72 |
73 | [Fact]
74 | public void SumInMonthlyHistogram()
75 | {
76 | var index = AddSimpleTestData();
77 |
78 | // First the standad NEST way
79 | var result = Client.Search(s => s.Index(index).Aggregations(a => a.DateHistogram("by_month",
80 | d => d.Field(x => x.Timestamp)
81 | .Interval(DateInterval.Month)
82 | .Aggregations(
83 | aggs => aggs.Sum("priceSum", dField => dField.Field(field => field.Price))))));
84 |
85 | var histogram = result.Aggregations.DateHistogram("by_month");
86 | Check.That(histogram.Buckets).HasSize(10);
87 | var firstMonth = histogram.Buckets.First();
88 | var priceSum = firstMonth.Sum("priceSum");
89 | Check.That(priceSum.Value.Value).Equals(10d);
90 |
91 |
92 | // Now with FluentNest
93 | result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
94 | .SumBy(x=>x.Price)
95 | .IntoDateHistogram(date => date.Timestamp, DateInterval.Month))
96 | );
97 |
98 | var histogram2 = result.Aggregations.GetDateHistogram(x => x.Timestamp);
99 | Check.That(histogram2).HasSize(10);
100 | Check.That(histogram2.All(x => x.GetSum(s => s.Price) == 10m)).IsTrue();
101 | }
102 |
103 | [Fact]
104 | public void MonthlySumHistogramFilteredOnDates()
105 | {
106 | var index = AddSimpleTestData();
107 | var start = new DateTime(2010, 1, 1);
108 | var end = new DateTime(2010, 4, 4);
109 |
110 | var result = Client.Search(sc => sc.Index(index)
111 | .FilterOn(f => f.Timestamp < end && f.Timestamp > start)
112 | .Aggregations(agg => agg
113 | .SumBy(x => x.Price)
114 | .IntoDateHistogram(date => date.Timestamp, DateInterval.Month)
115 | ));
116 |
117 | var histogram = result.Aggregations.GetDateHistogram(x => x.Timestamp);
118 |
119 | Check.That(histogram).HasSize(3);
120 | Client.DeleteIndex(index);
121 | }
122 |
123 | [Fact]
124 | public void StandardHistogramTest()
125 | {
126 | var index = AddSimpleTestData();
127 |
128 | var result = Client.Search(sc => sc.Index(index)
129 | .Aggregations(x => x
130 | .SumBy(y => y.Price)
131 | .IntoHistogram(y => y.Length, 5)
132 | ));
133 |
134 | var histogram = result.Aggregations.GetHistogram(x => x.Length);
135 | Check.That(histogram).HasSize(4);
136 | Client.DeleteIndex(index);
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/FluentNest.Tests/Model/Car.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FluentNest.Tests.Model
4 | {
5 | public enum EngineType
6 | {
7 | Diesel,
8 | Standard
9 | }
10 |
11 | public class Car
12 | {
13 | public Guid Id { get; set; }
14 | public String Name { get; set; }
15 | public decimal Price { get; set; }
16 | public double Length { get; set; }
17 | public bool Sold { get; set; }
18 | public DateTime Timestamp { get; set; }
19 | public String CarType { get; set; }
20 | public EngineType EngineType { get; set; }
21 | public EngineType? NullableEngineType { get; set; }
22 | public decimal? Weight { get; set; }
23 | public int? ConditionalRanking { get; set; }
24 | public decimal? PriceLimit { get; set; }
25 | public decimal Emissions { get; set; }
26 | public string Description { get; set; }
27 | public string Guid { get; set; }
28 | public bool Active { get; set; }
29 | public int Age { get; set; }
30 | public bool Enabled { get; set; }
31 |
32 | public string BIG_CASE_NAME { get; set; }
33 |
34 | // Of course cars don't have emails, but for my tests it's useful
35 | public string Email { get; set; }
36 | public DateTime? LastControlCheck { get; set; }
37 | public DateTime? LastAccident { get; set; }
38 |
39 | public string[] PreviousOwners { get; set; }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/FluentNest.Tests/Model/CarType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FluentNest.Tests.Model
4 | {
5 | public class CarType
6 | {
7 | public String Type { get; set; }
8 | public decimal Price { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/FluentNest.Tests/Model/User.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FluentNest.Tests.Model
4 | {
5 | public class User
6 | {
7 | public Guid Id { get; set; }
8 | public String Email { get; set; }
9 |
10 | public String Name { get; set; }
11 |
12 | public int Age { get; set; }
13 |
14 | public bool? Enabled { get; set; }
15 |
16 | public bool Active { get; set; }
17 |
18 | public DateTime CreationTime { get; set; }
19 |
20 | public String Nationality { get; set; }
21 |
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/FluentNest.Tests/PropertyNamesTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using FluentNest.Tests.Model;
4 | using Nest;
5 | using NFluent;
6 | using Tests;
7 | using Xunit;
8 |
9 | namespace FluentNest.Tests
10 | {
11 | public class PropertyNamesTests : TestsBase
12 | {
13 | public PropertyNamesTests()
14 | : base(null, connectionSettings => connectionSettings.DefaultFieldNameInferrer(p => p).DefaultTypeNameInferrer(p => p.Name))
15 | {
16 |
17 | }
18 |
19 | private string AddSimpleTestData()
20 | {
21 | var indexName = "index_" + Guid.NewGuid();
22 | Client.CreateIndex(indexName, x => x.Mappings(m => m
23 | .Map(t => t
24 | .Properties(prop => prop.Keyword(str => str.Name(s => s.Guid).Index()))
25 | .Properties(prop => prop.Keyword(str => str.Name(s => s.Email).Index()))
26 | )));
27 |
28 | for (int i = 0; i < 10; i++)
29 | {
30 | var car = new Car
31 | {
32 | Id = Guid.NewGuid(),
33 | BIG_CASE_NAME = "big" + i % 3
34 | };
35 |
36 | Client.Index(car, ind => ind.Index(indexName));
37 | }
38 | Client.Flush(indexName);
39 | return indexName;
40 | }
41 |
42 | [Fact]
43 | public void TestCasing()
44 | {
45 | var indexName = AddSimpleTestData();
46 | var filter = Filters.CreateFilter(f => f.BIG_CASE_NAME == "big1");
47 | filter = filter.AndFilteredOn(f => f.BIG_CASE_NAME != "big2");
48 | var sc = new SearchDescriptor().Index(indexName).FilterOn(filter);
49 | var query = Serialize(sc);
50 | Check.That(query).Contains("BIG_CASE_NAME");
51 | var cars = Client.Search(sc).Hits.Select(h => h.Source);
52 | Check.That(cars).HasSize(3);
53 | Client.DeleteIndex(indexName);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/FluentNest.Tests/StatisticsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using FluentNest.Tests.Model;
4 | using Nest;
5 | using NFluent;
6 | using Tests;
7 | using Xunit;
8 |
9 | namespace FluentNest.Tests
10 | {
11 | public class StatisticsTests : TestsBase
12 | {
13 | public string CreateTestIndex()
14 | {
15 | var indexName = "index_" + Guid.NewGuid();
16 | Client.CreateIndex(indexName, x => x.Mappings(
17 | m => m.Map(t => t
18 | .Properties(prop => prop.Keyword(str => str.Name(s => s.EngineType)))
19 | .Properties(prop => prop.Text(str => str.Name(s => s.CarType).Fielddata()))
20 | .Properties(prop => prop.Text(str => str.Name(s => s.Name).Fielddata()))
21 | )));
22 |
23 | return indexName;
24 | }
25 |
26 | public string AddSimpleTestData()
27 | {
28 | var indexName = CreateTestIndex();
29 |
30 | for (int i = 0; i < 10; i++)
31 | {
32 | var car = new Car
33 | {
34 | Id = Guid.NewGuid(),
35 | Timestamp = new DateTime(2010, i + 1, 1),
36 | Name = "Car" + i,
37 | Price = 10,
38 | Sold = i % 2 == 0 ? true : false,
39 | CarType = "Type" + i % 3,
40 | Length = i,
41 | EngineType = i % 2 == 0 ? EngineType.Diesel : EngineType.Standard,
42 | Weight = 5,
43 | ConditionalRanking = i % 2 == 0 ? null : (int?)i,
44 | Description = "Desc" + i,
45 | LastControlCheck = i % 2 == 0 ? new DateTime(2012, i + 1, 1) : (DateTime?)null,
46 | LastAccident = null
47 | };
48 |
49 | Client.Index(car, ind => ind.Index(indexName));
50 | }
51 | Client.Flush(indexName);
52 | return indexName;
53 | }
54 |
55 | [Fact]
56 | public void ConditionalStats_Without_FluentNest()
57 | {
58 | var index = AddSimpleTestData();
59 | var result = Client.Search(search => search.Index(index)
60 | .Aggregations(agg => agg
61 | .Filter("filterOne", f => f.Filter(innerFilter => innerFilter.Term(fd => fd.EngineType, EngineType.Diesel))
62 | .Aggregations(innerAgg => innerAgg.Sum("sumAgg", innerField =>
63 | innerField.Field(field => field.Price)))
64 | )
65 | .Filter("filterTwo", f => f.Filter(innerFilter => innerFilter.Term(fd => fd.CarType, "type1"))
66 | .Aggregations(innerAgg => innerAgg.Sum("sumAgg", innerField =>
67 | innerField.Field(field => field.Price)))
68 | )
69 | )
70 | );
71 |
72 | var sumAgg = result.Aggregations.Filter("filterOne");
73 | Check.That(sumAgg).IsNotNull();
74 | var sumValue = sumAgg.Sum("sumAgg");
75 | Check.That(sumValue.Value).Equals(50d);
76 |
77 | var sumAgg2 = result.Aggregations.Filter("filterTwo");
78 | Check.That(sumAgg2).IsNotNull();
79 | var sumValue2 = sumAgg2.Sum("sumAgg");
80 | Check.That(sumValue2.Value).Equals(30d);
81 | Client.DeleteIndex(index);
82 | }
83 |
84 | [Fact]
85 | public void CountAndCardinalityTest()
86 | {
87 | var index = AddSimpleTestData();
88 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
89 | .CountBy(x=>x.Price)
90 | .CardinalityBy(x => x.EngineType)
91 | ));
92 | var val = result.Aggregations.GetCount(x => x.Price);
93 | Check.That(val).Equals(10);
94 | var card = result.Aggregations.GetCardinality(x => x.EngineType);
95 | Check.That(card).Equals(2);
96 | Client.DeleteIndex(index);
97 | }
98 |
99 | [Fact]
100 | public void CardinalityFilterTest()
101 | {
102 | var index = AddSimpleTestData();
103 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
104 | .CardinalityBy(x => x.EngineType, x => x.EngineType == EngineType.Standard)
105 | ));
106 |
107 | var container = result.Aggregations.AsContainer();
108 | var card = result.Aggregations.GetCardinality(x => x.EngineType, x => x.EngineType == EngineType.Standard);
109 | Check.That(card).Equals(1);
110 | Check.That(container.GetCardinality(x => x.EngineType, x => x.EngineType == EngineType.Standard)).Equals(1);
111 | }
112 |
113 | [Fact]
114 | public void TestConditionalSum()
115 | {
116 | var index = AddSimpleTestData();
117 |
118 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
119 | .SumBy(x => x.Price, x => x.Sold == true)
120 | ));
121 |
122 | var sum = result.Aggregations.GetSum(x => x.Price, x => x.Sold == true);
123 | Check.That(sum).Equals(50m);
124 | Client.DeleteIndex(index);
125 | }
126 |
127 | [Fact]
128 | public void MultipleAggregationsInSingleAggregation()
129 | {
130 | var index = AddSimpleTestData();
131 |
132 | var result = Client.Search(s => s.Index(index).Aggregations(agg => agg
133 | .CountBy(x => x.Name, c => c.EngineType == EngineType.Diesel)
134 | .SumBy(x => x.Price)
135 | .AverageBy(x => x.Length)
136 | .CountBy(x => x.CarType)
137 | .CardinalityBy(x => x.EngineType))
138 | );
139 |
140 | var priceSum = result.Aggregations.GetSum(x => x.Price);
141 | var avgLength = result.Aggregations.GetAverage(x => x.Length);
142 | var count = result.Aggregations.GetCount(x => x.CarType);
143 | var typeOneCount = result.Aggregations.GetCount(x => x.Name, x => x.EngineType == EngineType.Diesel);
144 | var engineCardinality = result.Aggregations.GetCardinality(x => x.EngineType);
145 |
146 | Check.That(priceSum).Equals(100m);
147 | Check.That(avgLength).Equals(4.5d);
148 | Check.That(count).Equals(10);
149 | Check.That(typeOneCount).Equals(5);
150 | Check.That(engineCardinality).Equals(2);
151 | Client.DeleteIndex(index);
152 | }
153 |
154 | [Fact]
155 | public void MultipleAggregationsInSingleAggregation_ReversingOrder()
156 | {
157 | var index = AddSimpleTestData();
158 |
159 | var result = Client.Search(s => s.Index(index).Aggregations(agg => agg
160 | .SumBy(x => x.Price)
161 | .AverageBy(x => x.Length)
162 | .CountBy(x => x.CarType)
163 | .CountBy(x => x.Name, c => c.EngineType == EngineType.Diesel)
164 | .SumBy(x => x.Price, c => c.CarType == "type1")
165 | ));
166 |
167 | var priceSum = result.Aggregations.GetSum(x => x.Price);
168 | var avgLength = result.Aggregations.GetAverage(x => x.Length);
169 | var count = result.Aggregations.GetCount(x => x.CarType);
170 | var typeOneCount = result.Aggregations.GetCount(x => x.Name, x => x.EngineType == EngineType.Diesel);
171 | var car1PriceSum = result.Aggregations.GetSum(x => x.Price, x => x.CarType == "type1");
172 |
173 | var aggsContainer = result.Aggregations.AsContainer();
174 | var priceSum2 = aggsContainer.GetSum(x => x.Price);
175 | var avgLength2 = aggsContainer.GetAverage(x => x.Length);
176 | var count2 = aggsContainer.GetCount(x => x.CarType);
177 | var typeOneCount2 = aggsContainer.GetCount(x => x.Name, x => x.EngineType == EngineType.Diesel);
178 | var car1PriceSum2 = aggsContainer.GetSum(x => x.Price, x => x.CarType == "type1");
179 |
180 |
181 | Check.That(priceSum).Equals(100m);
182 | Check.That(avgLength).Equals(4.5d);
183 | Check.That(count).Equals(10);
184 | Check.That(typeOneCount).Equals(5);
185 | Check.That(car1PriceSum).Equals(30m);
186 |
187 | Check.That(priceSum2).Equals(100m);
188 | Check.That(avgLength2).Equals(4.5d);
189 | Check.That(count2).Equals(10);
190 | Check.That(typeOneCount2).Equals(5);
191 | Check.That(car1PriceSum2).Equals(30m);
192 | Client.DeleteIndex(index);
193 | }
194 |
195 | [Fact]
196 | public void SumOfNullableDecimal()
197 | {
198 | var index = AddSimpleTestData();
199 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg.SumBy(x => x.Weight)));
200 | var sum = result.Aggregations.GetSum(x => x.Weight);
201 |
202 | var container = result.Aggregations.AsContainer();
203 |
204 | var sum2 = container.GetSum(x => x.Weight);
205 |
206 | Check.That(sum).Equals(50m);
207 | Check.That(sum2).Equals(50m);
208 | Client.DeleteIndex(index);
209 | }
210 |
211 | [Fact]
212 | public void SumOfNamedField()
213 | {
214 | var index = AddSimpleTestData();
215 | var sc = new SearchDescriptor().Index(index).Aggregations(agg => agg.SumBy(x => x.GetFieldNamed("weight")));
216 | var result = Client.Search(sc);
217 | var sum = result.Aggs.GetSum(x => x.GetFieldNamed("weight"));
218 |
219 | var container = result.Aggs.AsContainer();
220 |
221 | var sum2 = container.GetSum(x => x.GetFieldNamed("weight"));
222 |
223 | Check.That(sum).Equals(50m);
224 | Check.That(sum2).Equals(50m);
225 | Client.DeleteIndex(index);
226 | }
227 |
228 | [Fact]
229 | public void SumOfConcatenatedNamedField()
230 | {
231 | DoTest("eight");
232 |
233 | void DoTest(string s)
234 | {
235 | var index = AddSimpleTestData();
236 | var sc = new SearchDescriptor().Index(index)
237 | .Aggregations(agg => agg.SumBy(x => x.GetFieldNamed("w" + s)));
238 | var result = Client.Search(sc);
239 | var sum = result.Aggs.GetSum(x => x.GetFieldNamed("w" + s));
240 |
241 | var container = result.Aggs.AsContainer();
242 |
243 | var sum2 = container.GetSum(x => x.GetFieldNamed("w" + s));
244 |
245 | Check.That(sum).Equals(50m);
246 | Check.That(sum2).Equals(50m);
247 | Client.DeleteIndex(index);
248 | }
249 | }
250 |
251 | [Fact]
252 | public void Condition_Equals_Not_Null_Test()
253 | {
254 | var index = AddSimpleTestData();
255 |
256 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
257 | .SumBy(x => x.Weight, x => x.ConditionalRanking.HasValue)
258 | ));
259 |
260 | var sum = result.Aggregations.GetSum(x => x.Weight, c => c.ConditionalRanking.HasValue);
261 |
262 | var container = result.Aggregations.AsContainer();
263 |
264 | var sum2 = container.GetSum(x => x.Weight,c=>c.ConditionalRanking.HasValue);
265 |
266 | Check.That(sum).Equals(25m);
267 | Check.That(sum2).Equals(25m);
268 | Client.DeleteIndex(index);
269 | }
270 |
271 | [Fact]
272 | public void Two_Conditional_Sums_Similar_Condition_One_More_Restrained()
273 | {
274 | var index = AddSimpleTestData();
275 |
276 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
277 | .SumBy(x => x.Weight, x => x.ConditionalRanking.HasValue)
278 | .SumBy(x => x.Weight, x => x.ConditionalRanking.HasValue && x.CarType == "Type1")
279 | ));
280 |
281 | var sum = result.Aggregations.GetSum(x => x.Weight, c => c.ConditionalRanking.HasValue);
282 | var sum2 = result.Aggregations.GetSum(x => x.Weight, c => c.ConditionalRanking.HasValue && c.CarType == "Type1");
283 |
284 | Check.That(sum).Equals(25m);
285 | Check.That(sum2).Equals(0m);
286 | Client.DeleteIndex(index);
287 | }
288 |
289 | [Fact]
290 | public void Percentiles_Test()
291 | {
292 | var index = AddSimpleTestData();
293 |
294 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
295 | .PercentilesBy(x=>x.Price)
296 | ));
297 |
298 | var percentiles = result.Aggregations.GetPercentile(x => x.Price);
299 | var container = result.Aggregations.AsContainer();
300 |
301 | Check.That(percentiles).HasSize(7);
302 | Check.That(container.GetPercentile(x => x.Price)).HasSize(7);
303 |
304 | Check.That(percentiles.Single(x => x.Percentile == 50.0).Value).Equals(10d);
305 | Client.DeleteIndex(index);
306 | }
307 |
308 | [Fact]
309 | public void MinMaxTest_DoubleField()
310 | {
311 | var index = AddSimpleTestData();
312 |
313 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
314 | .MinBy(x => x.Length)
315 | .MaxBy(x=> x.Length))
316 | );
317 |
318 | var container = result.Aggregations.AsContainer();
319 | var min = container.GetMin(x => x.Length);
320 | var max = container.GetMax(x => x.Length);
321 |
322 | Check.That(min).Equals(0d);
323 | Check.That(max).Equals(9d);
324 | Client.DeleteIndex(index);
325 | }
326 |
327 | [Fact]
328 | public void MinMaxTest_DateTimeField()
329 | {
330 | var index = AddSimpleTestData();
331 |
332 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
333 | .MinBy(x => x.Timestamp)
334 | .MaxBy(x => x.Timestamp))
335 | );
336 |
337 | var container = result.Aggregations.AsContainer();
338 | var min = container.GetMin(x => x.Timestamp);
339 | var max = container.GetMax(x => x.Timestamp);
340 |
341 | Check.That(min).Equals(new DateTime(2010, 1, 1));
342 | Check.That(max).Equals(new DateTime(2010, 10, 1));
343 | Client.DeleteIndex(index);
344 | }
345 |
346 | [Fact]
347 | public void MinMaxTest_NullableDateTimeField()
348 | {
349 | var index = AddSimpleTestData();
350 |
351 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
352 | .MinBy(x => x.LastControlCheck)
353 | .MaxBy(x => x.LastControlCheck))
354 | );
355 |
356 | var container = result.Aggregations.AsContainer();
357 | var min = container.GetMin(x => x.LastControlCheck);
358 | var max = container.GetMax(x => x.LastControlCheck);
359 |
360 | Check.That(min).Equals(new DateTime(2012, 1, 1));
361 | Check.That(max).Equals(new DateTime(2012, 9, 1));
362 | Client.DeleteIndex(index);
363 | }
364 |
365 | [Fact]
366 | public void MinMaxTest_NullableDateTimeField_AlwaysNull()
367 | {
368 | var index = AddSimpleTestData();
369 |
370 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
371 | .MinBy(x => x.LastAccident)
372 | .MaxBy(x => x.LastAccident))
373 | );
374 |
375 | var container = result.Aggregations.AsContainer();
376 | var min = container.GetMin(x => x.LastAccident);
377 | var max = container.GetMax(x => x.LastAccident);
378 |
379 | Check.That(min).Equals(null);
380 | Check.That(max).Equals(null);
381 | Client.DeleteIndex(index);
382 | }
383 |
384 | [Fact]
385 | public void Agg_On_NUllable_Field_With_No_Result()
386 | {
387 | //all price limit values are null - the result should be null
388 | var index = AddSimpleTestData();
389 |
390 | var result =
391 | Client.Search(
392 | search => search.Index(index).Take(10).Aggregations(agg => agg
393 | .MinBy(x => x.PriceLimit)
394 | .MaxBy(x=>x.PriceLimit)
395 | .PercentilesBy(x=> x.PriceLimit)));
396 |
397 | var container = result.Aggregations.AsContainer();
398 | var min = container.GetMin(x => x.PriceLimit);
399 | Check.That(min).IsNull();
400 |
401 | var max = container.GetMax(x => x.PriceLimit);
402 | Check.That(max).IsNull();
403 | }
404 |
405 | [Fact]
406 | public void Min_Max_Stats_By_Test()
407 | {
408 | //all price limit values are null - the result should be null
409 | var index = AddSimpleTestData();
410 |
411 | var result =
412 | Client.Search(
413 | search => search.Index(index)
414 | .Take(10).Aggregations(agg => agg
415 | .MinBy(x => x.Length)
416 | .MaxBy(x => x.Length)
417 | .StatsBy(x => x.Length)));
418 |
419 | var container = result.Aggregations.AsContainer();
420 | var min = container.GetMin(x => x.Length);
421 | var max = container.GetMax(x => x.Length);
422 | var stats = container.GetStats(x => x.Length);
423 | Check.That(stats.Min).Equals(0d);
424 | Check.That(stats.Max).Equals(9d);
425 | Check.That(min).Equals(0d);
426 | Check.That(max).Equals(9d);
427 | Client.DeleteIndex(index);
428 | }
429 |
430 | [Fact]
431 | public void FirstByTests()
432 | {
433 | //very stupid test, getting the single value of engine type when engine type is diesel
434 | var index = AddSimpleTestData();
435 |
436 | var result = Client.Search(sc => sc.Index(index).Aggregations(agg => agg
437 | .SumBy(x => x.Weight, x => x.ConditionalRanking.HasValue)
438 | .FirstBy(x => x.EngineType, c => c.EngineType == EngineType.Diesel)
439 | .FirstBy(x => x.CarType, c => c.Sold == true)
440 | .FirstBy(x => x.Length)
441 | ));
442 |
443 | var sum = result.Aggregations.GetSum(x => x.Weight, c => c.ConditionalRanking.HasValue);
444 | var engineType = result.Aggregations.GetFirstBy(x => x.EngineType, c => c.EngineType == EngineType.Diesel);
445 |
446 | //car type of first sold car
447 | var carType = result.Aggregations.GetFirstBy(x => x.CarType, c => c.Sold == true);
448 |
449 | var firstLength = result.Aggregations.GetFirstBy(x => x.Length);
450 |
451 | Check.That(sum).Equals(25m);
452 | Check.That(engineType).Equals(EngineType.Diesel);
453 | Check.That(carType).Equals("type0");
454 | Check.That(firstLength).Equals(0d);
455 |
456 | var container = result.Aggregations.AsContainer();
457 | var lengthFromContainer = container.GetFirstBy(x => x.Length);
458 | Check.That(lengthFromContainer).Equals(0d);
459 | Client.DeleteIndex(index);
460 | }
461 |
462 | [Fact]
463 | public void Non_Available_Conditional_Stat()
464 | {
465 | var index = CreateTestIndex();
466 |
467 | var result = Client.Search(search => search.Index(index).Take(10).Aggregations(agg => agg));
468 | var exception = Record.Exception(() => result.Aggregations.GetCount(x => x.CarType));
469 | Assert.NotNull(exception);
470 | Assert.IsType(exception);
471 | Check.That(exception.Message).Contains("No aggregations");
472 | }
473 | }
474 | }
475 |
--------------------------------------------------------------------------------
/FluentNest.Tests/TestsBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 | using Elasticsearch.Net;
8 | using Nest;
9 | using NFluent;
10 |
11 | namespace Tests
12 | {
13 | public class TestsBase
14 | {
15 | protected ElasticClient Client;
16 |
17 | private readonly Dictionary testResults;
18 |
19 | public TestsBase(ConnectionSettings.SourceSerializerFactory serializerFactory = null, Func additionalSettings = null)
20 | {
21 | var node = new Uri("http://localhost:9200");
22 | var connectionPool = new SingleNodeConnectionPool(node);
23 |
24 | var settings = new ConnectionSettings(connectionPool, serializerFactory)
25 | .DefaultIndex("fluentnesttests")
26 | .ThrowExceptions();
27 | if (additionalSettings != null)
28 | {
29 | settings = additionalSettings(settings);
30 | }
31 |
32 | Client = new ElasticClient(settings);
33 |
34 | testResults = LoadTestResults(this.GetType().Name);
35 | }
36 |
37 | public string Serialize(T entity)
38 | {
39 | using (var ms = new MemoryStream())
40 | {
41 | Client.SourceSerializer.Serialize(entity, ms);
42 | return Encoding.UTF8.GetString(ms.ToArray());
43 | }
44 | }
45 |
46 | private static readonly string AssemblyPath = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
47 |
48 | public Dictionary LoadTestResults(string className)
49 | {
50 | var fileName = Path.Combine(AssemblyPath, className + ".txt");
51 | if (!File.Exists(fileName))
52 | {
53 | return new Dictionary();
54 | }
55 |
56 | var testLines = File.ReadAllText(className + ".txt").Split("###".ToCharArray()).Select(x=>x.Trim());
57 | var values = testLines.Where(x=>x.Contains("***")).Select(x=>x.Trim()).Select(x =>
58 | {
59 | var testContent = x.Split("***".ToCharArray()).Where(y => !string.IsNullOrWhiteSpace(y)).ToArray();
60 | return new
61 | {
62 | Name = testContent[0].Trim(),
63 | Json = testContent[1].Trim()
64 | };
65 | });
66 |
67 | return values.ToDictionary(x => x.Name, y => y.Json);
68 | }
69 |
70 | public void CheckSD(SearchDescriptor sc, string testName) where T: class
71 | {
72 | var json = Serialize(sc);
73 | var escaped = string.Join("", json.Where(c => !char.IsWhiteSpace(c)));
74 |
75 | var expected = testResults[testName];
76 | var escapedExpected = string.Join("", expected.Where(c => !char.IsWhiteSpace(c)));
77 |
78 | Check.That(escaped).Equals(escapedExpected);
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/FluentNest.Tests/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/FluentNest.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/FluentNest.Tests/paket.references:
--------------------------------------------------------------------------------
1 | NEST
2 | NEST.JsonNetSerializer
3 | NFluent
4 | xunit
5 | xunit.runner.visualstudio
--------------------------------------------------------------------------------
/FluentNest.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25123.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{89A31101-BA64-47F0-AE83-4694EF2198C9}"
7 | ProjectSection(SolutionItems) = preProject
8 | paket.dependencies = paket.dependencies
9 | EndProjectSection
10 | EndProject
11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentNest", "FluentNest\FluentNest.csproj", "{4AA84915-4E63-48B5-B27B-579A56954B67}"
12 | EndProject
13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentNest.Tests", "FluentNest.Tests\FluentNest.Tests.csproj", "{7F4479CC-E444-4A63-AD47-762C7390E2FD}"
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0B655168-DED9-4758-9DCD-EDA18014E233}"
16 | ProjectSection(SolutionItems) = preProject
17 | paket.cmd = paket.cmd
18 | paket.dependencies = paket.dependencies
19 | paket.lock = paket.lock
20 | README.md = README.md
21 | run_es.ps1 = run_es.ps1
22 | tests.cmd = tests.cmd
23 | EndProjectSection
24 | EndProject
25 | Global
26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
27 | Debug|Any CPU = Debug|Any CPU
28 | Debug|x64 = Debug|x64
29 | Release|Any CPU = Release|Any CPU
30 | Release|x64 = Release|x64
31 | EndGlobalSection
32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
33 | {4AA84915-4E63-48B5-B27B-579A56954B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {4AA84915-4E63-48B5-B27B-579A56954B67}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {4AA84915-4E63-48B5-B27B-579A56954B67}.Debug|x64.ActiveCfg = Debug|Any CPU
36 | {4AA84915-4E63-48B5-B27B-579A56954B67}.Debug|x64.Build.0 = Debug|Any CPU
37 | {4AA84915-4E63-48B5-B27B-579A56954B67}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 | {4AA84915-4E63-48B5-B27B-579A56954B67}.Release|Any CPU.Build.0 = Release|Any CPU
39 | {4AA84915-4E63-48B5-B27B-579A56954B67}.Release|x64.ActiveCfg = Release|Any CPU
40 | {4AA84915-4E63-48B5-B27B-579A56954B67}.Release|x64.Build.0 = Release|Any CPU
41 | {7F4479CC-E444-4A63-AD47-762C7390E2FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {7F4479CC-E444-4A63-AD47-762C7390E2FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | {7F4479CC-E444-4A63-AD47-762C7390E2FD}.Debug|x64.ActiveCfg = Debug|Any CPU
44 | {7F4479CC-E444-4A63-AD47-762C7390E2FD}.Debug|x64.Build.0 = Debug|Any CPU
45 | {7F4479CC-E444-4A63-AD47-762C7390E2FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 | {7F4479CC-E444-4A63-AD47-762C7390E2FD}.Release|Any CPU.Build.0 = Release|Any CPU
47 | {7F4479CC-E444-4A63-AD47-762C7390E2FD}.Release|x64.ActiveCfg = Release|Any CPU
48 | {7F4479CC-E444-4A63-AD47-762C7390E2FD}.Release|x64.Build.0 = Release|Any CPU
49 | EndGlobalSection
50 | GlobalSection(SolutionProperties) = preSolution
51 | HideSolutionNode = FALSE
52 | EndGlobalSection
53 | EndGlobal
54 |
--------------------------------------------------------------------------------
/FluentNest/.gitignore:
--------------------------------------------------------------------------------
1 | *.v2.ncrunchproject
2 |
--------------------------------------------------------------------------------
/FluentNest/AggType.cs:
--------------------------------------------------------------------------------
1 | namespace FluentNest
2 | {
3 | public enum AggType
4 | {
5 | Min,
6 | Max,
7 | Average,
8 | Percentile,
9 | Sum,
10 | Count,
11 | Distinct,
12 | Cardinality,
13 | Stats,
14 | GroupBy,
15 | TopHits,
16 | First
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/FluentNest/AggsContainer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using Nest;
6 |
7 | namespace FluentNest
8 | {
9 | public class AggsContainer
10 | {
11 | private readonly AggregateDictionary aggs;
12 |
13 | public AggsContainer(AggregateDictionary aggs)
14 | {
15 | this.aggs = aggs;
16 | }
17 |
18 | public int GetCardinality(Expression> fieldGetter, Expression> filterRule = null)
19 | {
20 | return aggs.GetCardinality(fieldGetter, filterRule);
21 | }
22 |
23 | public TValue GetSum(Expression> fieldGetter, Expression> filterRule = null)
24 | {
25 | return aggs.GetSum(fieldGetter, filterRule);
26 | }
27 |
28 | public TValue GetAverage(Expression> fieldGetter)
29 | {
30 | return aggs.GetAverage(fieldGetter);
31 | }
32 |
33 | public TValue GetMin(Expression> fieldGetter)
34 | {
35 | return aggs.GetMin(fieldGetter);
36 | }
37 |
38 | public TValue GetMax(Expression> fieldGetter)
39 | {
40 | return aggs.GetMax(fieldGetter);
41 | }
42 |
43 | public int? GetCount(Expression> fieldGetter, Expression> filterRule = null)
44 | {
45 | return aggs.GetCount(fieldGetter, filterRule);
46 | }
47 |
48 | public IEnumerable GetDistinct(Expression> fieldGetter)
49 | {
50 | return aggs.GetDistinct(fieldGetter);
51 | }
52 |
53 | public IList GetPercentile(Expression> fieldGetter)
54 | {
55 | return aggs.GetPercentile(fieldGetter);
56 | }
57 |
58 | public StatsAggregate GetStats(Expression> fieldGetter)
59 | {
60 | return aggs.GetStats(fieldGetter);
61 | }
62 |
63 | public TValue GetFirstBy(Expression> fieldGetter,
64 | Expression> filterRule = null)
65 | {
66 | return aggs.GetFirstBy(fieldGetter, filterRule);
67 | }
68 |
69 | public IEnumerable> GetGroupBy(Expression> fieldGetter)
70 | {
71 | return aggs.GetGroupBy(fieldGetter);
72 | }
73 |
74 | public IEnumerable GetGroupBy(Expression> fieldGetter, Func