├── .config └── dotnet-tools.json ├── .devcontainer └── devcontainer.json ├── .github └── workflows │ └── build.yaml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE.md ├── README.md ├── analyzer ├── .editorconfig ├── .gitignore ├── Nall.NEST.MigtarionAnalyzer.CodeFixes │ ├── CodeFixResources.Designer.cs │ ├── CodeFixResources.resx │ ├── Nall.NEST.MigtarionAnalyzer.CodeFixes.csproj │ └── UseElasticsearchNetInsteadOfNestCodeFixProvider.cs ├── Nall.NEST.MigtarionAnalyzer.Package │ ├── Nall.NEST.MigtarionAnalyzer.Package.csproj │ └── tools │ │ ├── install.ps1 │ │ └── uninstall.ps1 ├── Nall.NEST.MigtarionAnalyzer.Test │ ├── Nall.NEST.MigtarionAnalyzer.Test.csproj │ ├── UseElasticsearchNetInsteadOfNestTests.cs │ └── Verifiers │ │ ├── CSharpAnalyzerVerifier`1+Test.cs │ │ ├── CSharpAnalyzerVerifier`1.cs │ │ ├── CSharpCodeFixVerifier`2+Test.cs │ │ ├── CSharpCodeFixVerifier`2.cs │ │ ├── CSharpCodeRefactoringVerifier`1+Test.cs │ │ ├── CSharpCodeRefactoringVerifier`1.cs │ │ ├── CSharpVerifierHelper.cs │ │ ├── VisualBasicAnalyzerVerifier`1+Test.cs │ │ ├── VisualBasicAnalyzerVerifier`1.cs │ │ ├── VisualBasicCodeFixVerifier`2+Test.cs │ │ ├── VisualBasicCodeFixVerifier`2.cs │ │ ├── VisualBasicCodeRefactoringVerifier`1+Test.cs │ │ └── VisualBasicCodeRefactoringVerifier`1.cs ├── Nall.NEST.MigtarionAnalyzer.Vsix │ ├── Nall.NEST.MigtarionAnalyzer.Vsix.csproj │ └── source.extension.vsixmanifest ├── Nall.NEST.MigtarionAnalyzer │ ├── Nall.NEST.MigtarionAnalyzer.csproj │ ├── Resources.Designer.cs │ ├── Resources.resx │ └── UseElasticsearchNetInsteadOfNest.cs ├── Playground │ ├── Playground.csproj │ ├── Playground.sln │ └── Program.cs ├── README.md ├── analyzer.sln └── global.json ├── assets ├── elastic-search-playground-demo.mp4 ├── elastic-search-playground-demo2.mp4 └── setup-elastic-infra.png └── src ├── _infra ├── azure-infra.ipynb ├── get-connection-string.ipynb └── setup-elastic-infrastructure.ipynb ├── elasticsearch-getting-started ├── 00-quick-start.ipynb ├── 01-keyword-querying-filtering.ipynb ├── 02-hybrid-search.ipynb ├── README.md ├── Utils.cs └── data.json └── nest-vs-elasticsearch ├── Utils.cs ├── common.ipynb ├── playground.ipynb ├── scroll.ipynb ├── search.ipynb ├── setup-clients.ipynb ├── setup-elastic-net.ipynb └── setup-nest.ipynb /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "microsoft.dotnet-interactive": { 6 | "version": "1.0.522904", 7 | "commands": [ 8 | "dotnet-interactive" 9 | ], 10 | "rollForward": false 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/dotnet 3 | { 4 | "name": "C# (.NET)", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0-bookworm", 7 | "features": { 8 | "ghcr.io/devcontainers/features/docker-in-docker:2": { 9 | "moby": true, 10 | "azureDnsAutoDetection": true, 11 | "installDockerBuildx": true, 12 | "installDockerComposeSwitch": true, 13 | "version": "latest", 14 | "dockerDashComposeVersion": "latest" 15 | }, 16 | "ghcr.io/devcontainers/features/dotnet:2": { 17 | "version": "latest" 18 | }, 19 | "ghcr.io/devcontainers/features/python:1": { 20 | "installTools": true, 21 | "installJupyterlab": true, 22 | "version": "latest" 23 | }, 24 | "ghcr.io/nikiforovall/devcontainer-features/dotnet-csharpier:1": {} 25 | }, 26 | 27 | // Features to add to the dev container. More info: https://containers.dev/features. 28 | // "features": {}, 29 | 30 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 31 | // "forwardPorts": [5000, 5001], 32 | // "portsAttributes": { 33 | // "5001": { 34 | // "protocol": "https" 35 | // } 36 | // } 37 | 38 | // Use 'postCreateCommand' to run commands after the container is created. 39 | "postCreateCommand": "dotnet tool restore && dotnet interactive jupyter install", 40 | "customizations": { 41 | "vscode": { 42 | "extensions": [ 43 | "csharpier.csharpier-vscode", 44 | "ms-dotnettools.dotnet-interactive-vscode", 45 | "Supermaven.supermaven" 46 | ] 47 | } 48 | } 49 | 50 | // Configure tool-specific properties. 51 | // "customizations": {}, 52 | 53 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 54 | // "remoteUser": "root" 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - '**/*.md' 9 | - '**/*.gitignore' 10 | - '**/*.gitattributes' 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | if: github.event_name == 'push' && contains(toJson(github.event.commits), '***NO_CI***') == false && contains(toJson(github.event.commits), '[ci skip]') == false && contains(toJson(github.event.commits), '[skip ci]') == false 16 | name: Build 17 | runs-on: windows-latest 18 | env: 19 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 20 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 21 | DOTNET_NOLOGO: true 22 | DOTNET_GENERATE_ASPNET_CERTIFICATE: false 23 | DOTNET_ADD_GLOBAL_TOOLS_TO_PATH: false 24 | DOTNET_MULTILEVEL_LOOKUP: 0 25 | PACKAGE_PROJECT: analyzer\Nall.NEST.MigtarionAnalyzer.Package 26 | VSIX_PROJECT: analyzer\Nall.NEST.MigtarionAnalyzer.Vsix 27 | 28 | steps: 29 | - uses: actions/checkout@v4.1.7 30 | 31 | - name: Setup .NET Core SDK 32 | uses: actions/setup-dotnet@v4.0.1 33 | with: 34 | dotnet-version: 8.0.401 35 | 36 | - name: Setup MSBuild 37 | uses: microsoft/setup-msbuild@v1 38 | 39 | - name: Setup NuGet 40 | uses: NuGet/setup-nuget@v2.0.0 41 | 42 | - name: Add NuGet Source 43 | run: nuget sources Add -Name "Github" -Source https://nuget.pkg.github.com/nikiforovall/index.json -UserName nikiforovall -Password ${{secrets.GITHUB_TOKEN}} 44 | 45 | - name: Build NuGet Package 46 | run: | 47 | msbuild /restore ${{ env.PACKAGE_PROJECT }} /p:Configuration=Release /p:PackageOutputPath=${{ github.workspace }}\artifacts 48 | 49 | - name: Build VSIX Package 50 | run: | 51 | msbuild /restore ${{ env.VSIX_PROJECT }} /p:Configuration=Release /p:OutDir=${{ github.workspace }}\artifacts 52 | 53 | - name: Push to GitHub Packages 54 | run: nuget push ${{ github.workspace }}\artifacts\*.nupkg -Source "Github" -SkipDuplicate 55 | 56 | # upload artifacts 57 | - name: Upload artifacts 58 | uses: actions/upload-artifact@v3.2.1 59 | with: 60 | name: release-pacakges 61 | path: | 62 | ${{ github.workspace }}\artifacts\**\*.vsix 63 | ${{ github.workspace }}\artifacts\**\*.nupkg 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from `dotnet new gitignore` 5 | 6 | # dotenv files 7 | .env 8 | 9 | # User-specific files 10 | *.rsuser 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Mono auto generated files 20 | mono_crash.* 21 | 22 | # Build results 23 | [Dd]ebug/ 24 | [Dd]ebugPublic/ 25 | [Rr]elease/ 26 | [Rr]eleases/ 27 | x64/ 28 | x86/ 29 | [Ww][Ii][Nn]32/ 30 | [Aa][Rr][Mm]/ 31 | [Aa][Rr][Mm]64/ 32 | bld/ 33 | [Bb]in/ 34 | [Oo]bj/ 35 | [Ll]og/ 36 | [Ll]ogs/ 37 | 38 | # Visual Studio 2015/2017 cache/options directory 39 | .vs/ 40 | # Uncomment if you have tasks that create the project's static files in wwwroot 41 | #wwwroot/ 42 | 43 | # Visual Studio 2017 auto generated files 44 | Generated\ Files/ 45 | 46 | # MSTest test Results 47 | [Tt]est[Rr]esult*/ 48 | [Bb]uild[Ll]og.* 49 | 50 | # NUnit 51 | *.VisualState.xml 52 | TestResult.xml 53 | nunit-*.xml 54 | 55 | # Build Results of an ATL Project 56 | [Dd]ebugPS/ 57 | [Rr]eleasePS/ 58 | dlldata.c 59 | 60 | # Benchmark Results 61 | BenchmarkDotNet.Artifacts/ 62 | 63 | # .NET 64 | project.lock.json 65 | project.fragment.lock.json 66 | artifacts/ 67 | 68 | # Tye 69 | .tye/ 70 | 71 | # ASP.NET Scaffolding 72 | ScaffoldingReadMe.txt 73 | 74 | # StyleCop 75 | StyleCopReport.xml 76 | 77 | # Files built by Visual Studio 78 | *_i.c 79 | *_p.c 80 | *_h.h 81 | *.ilk 82 | *.meta 83 | *.obj 84 | *.iobj 85 | *.pch 86 | *.pdb 87 | *.ipdb 88 | *.pgc 89 | *.pgd 90 | *.rsp 91 | *.sbr 92 | *.tlb 93 | *.tli 94 | *.tlh 95 | *.tmp 96 | *.tmp_proj 97 | *_wpftmp.csproj 98 | *.log 99 | *.tlog 100 | *.vspscc 101 | *.vssscc 102 | .builds 103 | *.pidb 104 | *.svclog 105 | *.scc 106 | 107 | # Chutzpah Test files 108 | _Chutzpah* 109 | 110 | # Visual C++ cache files 111 | ipch/ 112 | *.aps 113 | *.ncb 114 | *.opendb 115 | *.opensdf 116 | *.sdf 117 | *.cachefile 118 | *.VC.db 119 | *.VC.VC.opendb 120 | 121 | # Visual Studio profiler 122 | *.psess 123 | *.vsp 124 | *.vspx 125 | *.sap 126 | 127 | # Visual Studio Trace Files 128 | *.e2e 129 | 130 | # TFS 2012 Local Workspace 131 | $tf/ 132 | 133 | # Guidance Automation Toolkit 134 | *.gpState 135 | 136 | # ReSharper is a .NET coding add-in 137 | _ReSharper*/ 138 | *.[Rr]e[Ss]harper 139 | *.DotSettings.user 140 | 141 | # TeamCity is a build add-in 142 | _TeamCity* 143 | 144 | # DotCover is a Code Coverage Tool 145 | *.dotCover 146 | 147 | # AxoCover is a Code Coverage Tool 148 | .axoCover/* 149 | !.axoCover/settings.json 150 | 151 | # Coverlet is a free, cross platform Code Coverage Tool 152 | coverage*.json 153 | coverage*.xml 154 | coverage*.info 155 | 156 | # Visual Studio code coverage results 157 | *.coverage 158 | *.coveragexml 159 | 160 | # NCrunch 161 | _NCrunch_* 162 | .*crunch*.local.xml 163 | nCrunchTemp_* 164 | 165 | # MightyMoose 166 | *.mm.* 167 | AutoTest.Net/ 168 | 169 | # Web workbench (sass) 170 | .sass-cache/ 171 | 172 | # Installshield output folder 173 | [Ee]xpress/ 174 | 175 | # DocProject is a documentation generator add-in 176 | DocProject/buildhelp/ 177 | DocProject/Help/*.HxT 178 | DocProject/Help/*.HxC 179 | DocProject/Help/*.hhc 180 | DocProject/Help/*.hhk 181 | DocProject/Help/*.hhp 182 | DocProject/Help/Html2 183 | DocProject/Help/html 184 | 185 | # Click-Once directory 186 | publish/ 187 | 188 | # Publish Web Output 189 | *.[Pp]ublish.xml 190 | *.azurePubxml 191 | # Note: Comment the next line if you want to checkin your web deploy settings, 192 | # but database connection strings (with potential passwords) will be unencrypted 193 | *.pubxml 194 | *.publishproj 195 | 196 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 197 | # checkin your Azure Web App publish settings, but sensitive information contained 198 | # in these scripts will be unencrypted 199 | PublishScripts/ 200 | 201 | # NuGet Packages 202 | *.nupkg 203 | # NuGet Symbol Packages 204 | *.snupkg 205 | # The packages folder can be ignored because of Package Restore 206 | **/[Pp]ackages/* 207 | # except build/, which is used as an MSBuild target. 208 | !**/[Pp]ackages/build/ 209 | # Uncomment if necessary however generally it will be regenerated when needed 210 | #!**/[Pp]ackages/repositories.config 211 | # NuGet v3's project.json files produces more ignorable files 212 | *.nuget.props 213 | *.nuget.targets 214 | 215 | # Microsoft Azure Build Output 216 | csx/ 217 | *.build.csdef 218 | 219 | # Microsoft Azure Emulator 220 | ecf/ 221 | rcf/ 222 | 223 | # Windows Store app package directories and files 224 | AppPackages/ 225 | BundleArtifacts/ 226 | Package.StoreAssociation.xml 227 | _pkginfo.txt 228 | *.appx 229 | *.appxbundle 230 | *.appxupload 231 | 232 | # Visual Studio cache files 233 | # files ending in .cache can be ignored 234 | *.[Cc]ache 235 | # but keep track of directories ending in .cache 236 | !?*.[Cc]ache/ 237 | 238 | # Others 239 | ClientBin/ 240 | ~$* 241 | *~ 242 | *.dbmdl 243 | *.dbproj.schemaview 244 | *.jfm 245 | *.pfx 246 | *.publishsettings 247 | orleans.codegen.cs 248 | 249 | # Including strong name files can present a security risk 250 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 251 | #*.snk 252 | 253 | # Since there are multiple workflows, uncomment next line to ignore bower_components 254 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 255 | #bower_components/ 256 | 257 | # RIA/Silverlight projects 258 | Generated_Code/ 259 | 260 | # Backup & report files from converting an old project file 261 | # to a newer Visual Studio version. Backup files are not needed, 262 | # because we have git ;-) 263 | _UpgradeReport_Files/ 264 | Backup*/ 265 | UpgradeLog*.XML 266 | UpgradeLog*.htm 267 | ServiceFabricBackup/ 268 | *.rptproj.bak 269 | 270 | # SQL Server files 271 | *.mdf 272 | *.ldf 273 | *.ndf 274 | 275 | # Business Intelligence projects 276 | *.rdl.data 277 | *.bim.layout 278 | *.bim_*.settings 279 | *.rptproj.rsuser 280 | *- [Bb]ackup.rdl 281 | *- [Bb]ackup ([0-9]).rdl 282 | *- [Bb]ackup ([0-9][0-9]).rdl 283 | 284 | # Microsoft Fakes 285 | FakesAssemblies/ 286 | 287 | # GhostDoc plugin setting file 288 | *.GhostDoc.xml 289 | 290 | # Node.js Tools for Visual Studio 291 | .ntvs_analysis.dat 292 | node_modules/ 293 | 294 | # Visual Studio 6 build log 295 | *.plg 296 | 297 | # Visual Studio 6 workspace options file 298 | *.opt 299 | 300 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 301 | *.vbw 302 | 303 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 304 | *.vbp 305 | 306 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 307 | *.dsw 308 | *.dsp 309 | 310 | # Visual Studio 6 technical files 311 | *.ncb 312 | *.aps 313 | 314 | # Visual Studio LightSwitch build output 315 | **/*.HTMLClient/GeneratedArtifacts 316 | **/*.DesktopClient/GeneratedArtifacts 317 | **/*.DesktopClient/ModelManifest.xml 318 | **/*.Server/GeneratedArtifacts 319 | **/*.Server/ModelManifest.xml 320 | _Pvt_Extensions 321 | 322 | # Paket dependency manager 323 | .paket/paket.exe 324 | paket-files/ 325 | 326 | # FAKE - F# Make 327 | .fake/ 328 | 329 | # CodeRush personal settings 330 | .cr/personal 331 | 332 | # Python Tools for Visual Studio (PTVS) 333 | __pycache__/ 334 | *.pyc 335 | 336 | # Cake - Uncomment if you are using it 337 | # tools/** 338 | # !tools/packages.config 339 | 340 | # Tabs Studio 341 | *.tss 342 | 343 | # Telerik's JustMock configuration file 344 | *.jmconfig 345 | 346 | # BizTalk build output 347 | *.btp.cs 348 | *.btm.cs 349 | *.odx.cs 350 | *.xsd.cs 351 | 352 | # OpenCover UI analysis results 353 | OpenCover/ 354 | 355 | # Azure Stream Analytics local run output 356 | ASALocalRun/ 357 | 358 | # MSBuild Binary and Structured Log 359 | *.binlog 360 | 361 | # NVidia Nsight GPU debugger configuration file 362 | *.nvuser 363 | 364 | # MFractors (Xamarin productivity tool) working folder 365 | .mfractor/ 366 | 367 | # Local History for Visual Studio 368 | .localhistory/ 369 | 370 | # Visual Studio History (VSHistory) files 371 | .vshistory/ 372 | 373 | # BeatPulse healthcheck temp database 374 | healthchecksdb 375 | 376 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 377 | MigrationBackup/ 378 | 379 | # Ionide (cross platform F# VS Code tools) working folder 380 | .ionide/ 381 | 382 | # Fody - auto-generated XML schema 383 | FodyWeavers.xsd 384 | 385 | # VS Code files for those working on multiple tools 386 | .vscode/* 387 | !.vscode/settings.json 388 | !.vscode/tasks.json 389 | !.vscode/launch.json 390 | !.vscode/extensions.json 391 | *.code-workspace 392 | 393 | # Local History for Visual Studio Code 394 | .history/ 395 | 396 | # Windows Installer files from build outputs 397 | *.cab 398 | *.msi 399 | *.msix 400 | *.msm 401 | *.msp 402 | 403 | # JetBrains Rider 404 | *.sln.iml 405 | .idea/ 406 | 407 | ## 408 | ## Visual studio for Mac 409 | ## 410 | 411 | 412 | # globs 413 | Makefile.in 414 | *.userprefs 415 | *.usertasks 416 | config.make 417 | config.status 418 | aclocal.m4 419 | install-sh 420 | autom4te.cache/ 421 | *.tar.gz 422 | tarballs/ 423 | test-results/ 424 | 425 | # Mac bundle stuff 426 | *.dmg 427 | *.app 428 | 429 | # content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore 430 | # General 431 | .DS_Store 432 | .AppleDouble 433 | .LSOverride 434 | 435 | # Icon must end with two \r 436 | Icon 437 | 438 | 439 | # Thumbnails 440 | ._* 441 | 442 | # Files that might appear in the root of a volume 443 | .DocumentRevisions-V100 444 | .fseventsd 445 | .Spotlight-V100 446 | .TemporaryItems 447 | .Trashes 448 | .VolumeIcon.icns 449 | .com.apple.timemachine.donotpresent 450 | 451 | # Directories potentially created on remote AFP share 452 | .AppleDB 453 | .AppleDesktop 454 | Network Trash Folder 455 | Temporary Items 456 | .apdisk 457 | 458 | # content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore 459 | # Windows thumbnail cache files 460 | Thumbs.db 461 | ehthumbs.db 462 | ehthumbs_vista.db 463 | 464 | # Dump file 465 | *.stackdump 466 | 467 | # Folder config file 468 | [Dd]esktop.ini 469 | 470 | # Recycle Bin used on file shares 471 | $RECYCLE.BIN/ 472 | 473 | # Windows Installer files 474 | *.cab 475 | *.msi 476 | *.msix 477 | *.msm 478 | *.msp 479 | 480 | # Windows shortcuts 481 | *.lnk 482 | 483 | # Vim temporary swap files 484 | *.swp 485 | 486 | report -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["supermaven.supermaven"], 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "activityBar.background": "#0C3333", 4 | "titleBar.activeBackground": "#114747", 5 | "titleBar.activeForeground": "#F2FCFC" 6 | } 7 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Oleksii Nikiforov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elasticsearch .NET Playground 2 | 3 | This playground is a collection of notebooks that demonstrate how to use `Elastic.Clients.Elasticsearch` and `NEST` clients. 4 | 5 | You may want to use this playground to: 6 | * Learn more about `Elastic.Clients.Elasticsearch` and `NEST` clients 7 | * You want to migrate your existing code from `NEST` to `Elastic.Clients.Elasticsearch`. 8 | 9 | > See [playground.ipynb](./src/nest-vs-elasticsearch/playground.ipynb) to get started. 10 | 11 | ![setup-elastic-infra](./assets/setup-elastic-infra.png) 12 | 13 | ## Configure 14 | 15 | To configure the playground, set the `PLAYGROUND_CONNECTION_STRING=https://elastic:elastic@127.0.0.1:9200/` in .env file in the root of the project. If you don't do it, you will be prompted to enter the connection string every time you set up the client 16 | 17 | ## Analyzer [![Build](https://github.com/NikiforovAll/elasticsearch-dotnet-playground/actions/workflows/build.yaml/badge.svg)](https://github.com/NikiforovAll/elasticsearch-dotnet-playground/actions/workflows/build.yaml) 18 | 19 | This repository contains [Nall.NEST.MigtarionAnalyzer](https://www.nuget.org/packages/Nall.NEST.MigtarionAnalyzer) analyzer that helps with migration from `Nest` to `Elastic.Clients.Elasticsearch`. 20 | 21 | ```bash 22 | dotnet add package Nall.NEST.MigtarionAnalyzer --version 1.2.0 23 | ``` 24 | 25 | ## Devcontainer 26 | 27 | You can use the devcontainer to get started with the playground. It will install the required tools and libraries. 28 | 29 | > See [.devcontainer/devcontainer.json](./.devcontainer/devcontainer.json) to get started. 30 | 31 | This command will confirm that Jupyter now supports C# notebooks: 32 | 33 | ```bash 34 | jupyter kernelspec list 35 | ``` 36 | 37 | Enter the notebooks folder, and run this to launch the browser interface: 38 | 39 | ```bash 40 | jupyter-lab 41 | ``` 42 | 43 | ## References 44 | 45 | * Elasticsearch.NET (latest) - 46 | * Migration Guide - 47 | * Elasticsearch.NET - 48 | * NEST + Elasticsearch.NET in one doc - 49 | * .NET Interactive | Samples - 50 | * .NET Interactive - 51 | * Elasticsearch Labs 52 | * 53 | * 54 | * -------------------------------------------------------------------------------- /analyzer/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Don't use tabs for indentation. 7 | [*] 8 | indent_style = space 9 | # (Please don't specify an indent_size here; that has too many unintended consequences.) 10 | 11 | [*.{tf,tfvars,hcl}] 12 | indent_size = 2 13 | 14 | # Code files 15 | [*.{cs,csx,vb,vbx}] 16 | indent_size = 4 17 | insert_final_newline = true 18 | charset = utf-8-bom 19 | 20 | # XML project files 21 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] 22 | indent_size = 2 23 | 24 | # XML config files 25 | [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] 26 | indent_size = 2 27 | 28 | # JSON files 29 | [*.json] 30 | indent_size = 2 31 | 32 | # Powershell files 33 | [*.ps1] 34 | indent_size = 2 35 | 36 | # Dotnet code style settings: 37 | [*.{cs,vb}] 38 | dotnet_diagnostic.xUnit2018.severity = none # "do not compare an object's exact type to the abstract class" is a valid assert, but very noisy right now 39 | 40 | # Sort using and Import directives with System.* appearing first 41 | dotnet_sort_system_directives_first = true 42 | dotnet_separate_import_directive_groups = false 43 | # Avoid "this." and "Me." if not necessary 44 | dotnet_style_qualification_for_field = false:refactoring 45 | dotnet_style_qualification_for_property = false:refactoring 46 | dotnet_style_qualification_for_method = false:refactoring 47 | dotnet_style_qualification_for_event = false:refactoring 48 | 49 | # Use language keywords instead of framework type names for type references 50 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 51 | dotnet_style_predefined_type_for_member_access = true:suggestion 52 | 53 | # Suggest more modern language features when available 54 | dotnet_style_object_initializer = true:suggestion 55 | dotnet_style_collection_initializer = true:suggestion 56 | dotnet_style_coalesce_expression = true:suggestion 57 | dotnet_style_null_propagation = true:suggestion 58 | dotnet_style_explicit_tuple_names = true:suggestion 59 | 60 | # Whitespace options 61 | dotnet_style_allow_multiple_blank_lines_experimental = false 62 | 63 | # Non-private static fields are PascalCase 64 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion 65 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields 66 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style 67 | 68 | dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field 69 | dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected 70 | dotnet_naming_symbols.non_private_static_fields.required_modifiers = static 71 | 72 | dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case 73 | 74 | # Non-private readonly fields are PascalCase 75 | dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion 76 | dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields 77 | dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style 78 | 79 | dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field 80 | dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected 81 | dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly 82 | 83 | dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case 84 | 85 | # Constants are PascalCase 86 | dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion 87 | dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants 88 | dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style 89 | 90 | dotnet_naming_symbols.constants.applicable_kinds = field, local 91 | dotnet_naming_symbols.constants.required_modifiers = const 92 | 93 | dotnet_naming_style.constant_style.capitalization = pascal_case 94 | 95 | # Static fields are camelCase and start with s_ 96 | dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion 97 | dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields 98 | dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style 99 | 100 | dotnet_naming_symbols.static_fields.applicable_kinds = field 101 | dotnet_naming_symbols.static_fields.required_modifiers = static 102 | 103 | dotnet_naming_style.static_field_style.capitalization = camel_case 104 | dotnet_naming_style.static_field_style.required_prefix = s_ 105 | 106 | # Instance fields are camelCase and start with _ 107 | dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion 108 | dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields 109 | dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style 110 | 111 | dotnet_naming_symbols.instance_fields.applicable_kinds = field 112 | 113 | dotnet_naming_style.instance_field_style.capitalization = camel_case 114 | dotnet_naming_style.instance_field_style.required_prefix = _ 115 | 116 | # Locals and parameters are camelCase 117 | dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion 118 | dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters 119 | dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style 120 | 121 | dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local 122 | 123 | dotnet_naming_style.camel_case_style.capitalization = camel_case 124 | 125 | # Local functions are PascalCase 126 | dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion 127 | dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions 128 | dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style 129 | 130 | dotnet_naming_symbols.local_functions.applicable_kinds = local_function 131 | 132 | dotnet_naming_style.local_function_style.capitalization = pascal_case 133 | 134 | # By default, name items with PascalCase 135 | dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion 136 | dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members 137 | dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style 138 | 139 | dotnet_naming_symbols.all_members.applicable_kinds = * 140 | 141 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 142 | 143 | # IDE0055: Fix formatting 144 | # Workaround for https://github.com/dotnet/roslyn/issues/70570 145 | dotnet_diagnostic.IDE0055.severity = warning 146 | 147 | # CSharp code style settings: 148 | [*.cs] 149 | max_line_length=100 150 | # Newline settings 151 | csharp_new_line_before_open_brace = all 152 | csharp_new_line_before_else = true 153 | csharp_new_line_before_catch = true 154 | csharp_new_line_before_finally = true 155 | csharp_new_line_before_members_in_object_initializers = true 156 | csharp_new_line_before_members_in_anonymous_types = true 157 | csharp_new_line_between_query_expression_clauses = true 158 | 159 | # Indentation preferences 160 | csharp_indent_block_contents = true 161 | csharp_indent_braces = false 162 | csharp_indent_case_contents = true 163 | csharp_indent_case_contents_when_block = true 164 | csharp_indent_switch_labels = true 165 | csharp_indent_labels = flush_left 166 | 167 | # Whitespace options 168 | csharp_style_allow_embedded_statements_on_same_line_experimental = false 169 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false 170 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false 171 | csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false 172 | csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false 173 | 174 | # Prefer "var" everywhere 175 | csharp_style_var_for_built_in_types = true:suggestion 176 | csharp_style_var_when_type_is_apparent = true:suggestion 177 | csharp_style_var_elsewhere = true:suggestion 178 | 179 | # Prefer method-like constructs to have a block body 180 | csharp_style_expression_bodied_methods = false:none 181 | csharp_style_expression_bodied_constructors = false:none 182 | csharp_style_expression_bodied_operators = false:none 183 | 184 | # Prefer property-like constructs to have an expression-body 185 | csharp_style_expression_bodied_properties = true:none 186 | csharp_style_expression_bodied_indexers = true:none 187 | csharp_style_expression_bodied_accessors = true:none 188 | 189 | # Suggest more modern language features when available 190 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 191 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 192 | csharp_style_inlined_variable_declaration = true:suggestion 193 | csharp_style_throw_expression = true:suggestion 194 | csharp_style_conditional_delegate_call = true:suggestion 195 | csharp_style_prefer_extended_property_pattern = true:suggestion 196 | 197 | # Space preferences 198 | csharp_space_after_cast = false 199 | csharp_space_after_colon_in_inheritance_clause = true 200 | csharp_space_after_comma = true 201 | csharp_space_after_dot = false 202 | csharp_space_after_keywords_in_control_flow_statements = true 203 | csharp_space_after_semicolon_in_for_statement = true 204 | csharp_space_around_binary_operators = before_and_after 205 | csharp_space_around_declaration_statements = do_not_ignore 206 | csharp_space_before_colon_in_inheritance_clause = true 207 | csharp_space_before_comma = false 208 | csharp_space_before_dot = false 209 | csharp_space_before_open_square_brackets = false 210 | csharp_space_before_semicolon_in_for_statement = false 211 | csharp_space_between_empty_square_brackets = false 212 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 213 | csharp_space_between_method_call_name_and_opening_parenthesis = false 214 | csharp_space_between_method_call_parameter_list_parentheses = false 215 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 216 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 217 | csharp_space_between_method_declaration_parameter_list_parentheses = false 218 | csharp_space_between_parentheses = false 219 | csharp_space_between_square_brackets = false 220 | 221 | # Blocks are allowed 222 | csharp_prefer_braces = true:error 223 | csharp_preserve_single_line_blocks = true 224 | csharp_preserve_single_line_statements = false 225 | csharp_place_attribute_on_same_line = never 226 | csharp_max_initializer_elements_on_line = 1 227 | csharp_max_array_initializer_elements_on_line = 1 228 | 229 | # CS4014: Because this call is not awaited, execution of the current method continues before the call is completed 230 | dotnet_diagnostic.CS4014.severity = error 231 | 232 | # VSTHRD200: Use "Async" suffix for async methods 233 | dotnet_diagnostic.VSTHRD200.severity = error 234 | 235 | # VSTHRD011: Use AsyncLazy instead 236 | dotnet_diagnostic.VSTHRD011.severity = none 237 | 238 | # Consider calling Task.ConfigureAwait(Boolean) to signal your intention for continuation. 239 | dotnet_diagnostic.CA2007.severity = none 240 | 241 | # CA1848: Use the LoggerMessage delegates 242 | # Consider to use this approach later: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/loggermessage?view=aspnetcore-6.0 243 | # BUG: broken in dotnet7, will be fixed in dotnet8: https://github.com/dotnet/roslyn-analyzers/issues/6281 244 | dotnet_diagnostic.CA1848.severity = none 245 | 246 | # CA1308: Normalize strings to uppercase 247 | dotnet_diagnostic.CA1308.severity = none 248 | 249 | # CS8618: Non-nullable field is uninitialized. Consider declaring as nullable. 250 | dotnet_diagnostic.CS8618.severity = none 251 | 252 | # CA1707: Identifiers should not contain underscores 253 | dotnet_diagnostic.CA1707.severity = none 254 | 255 | # CA1819: Properties should not return arrays 256 | dotnet_diagnostic.CA1819.severity = none 257 | 258 | # IDE1006: Naming Styles 259 | dotnet_diagnostic.IDE1006.severity = warning 260 | 261 | # VSTHRD111: Use ConfigureAwait(bool) 262 | dotnet_diagnostic.VSTHRD111.severity = silent 263 | 264 | # CA2007: Consider calling ConfigureAwait on the awaited task 265 | dotnet_diagnostic.CA2007.severity = silent 266 | 267 | # CA1716: Conflicts with the reserved language keyword 268 | dotnet_diagnostic.CA1716.severity = silent 269 | 270 | # IDE0051: Remove unused private members 271 | dotnet_diagnostic.IDE0051.severity = warning 272 | 273 | # Default severity for analyzer diagnostics with category 'CodeQuality' 274 | dotnet_analyzer_diagnostic.category-CodeQuality.severity = warning 275 | 276 | # CS1591: Missing XML comment for publicly visible type or member 277 | dotnet_diagnostic.CS1591.severity = silent 278 | 279 | # IDE0011: Add braces 280 | dotnet_diagnostic.IDE0011.severity = suggestion 281 | 282 | # IDE0005: Using directive is unnecessary. 283 | dotnet_diagnostic.IDE0005.severity = warning 284 | -------------------------------------------------------------------------------- /analyzer/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # Ionide 256 | .ionide/ 257 | 258 | # JetBrains Rider 259 | .idea/ 260 | *.sln.iml 261 | 262 | # CodeRush 263 | .cr/ 264 | 265 | # Python Tools for Visual Studio (PTVS) 266 | __pycache__/ 267 | *.pyc 268 | 269 | # Local .terraform directories 270 | **/.terraform/* 271 | 272 | # .tfstate files 273 | *.tfstate 274 | *.tfstate.* 275 | 276 | # Crash log files 277 | crash.log 278 | crash.*.log 279 | 280 | # Ignore override files as they are usually used to override resources locally and so 281 | # are not checked in 282 | override.tf 283 | override.tf.json 284 | *_override.tf 285 | *_override.tf.json 286 | 287 | # Include override files you do wish to add to version control using negated pattern 288 | # 289 | # !example_override.tf 290 | 291 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 292 | # example: *tfplan* 293 | 294 | # Ignore CLI configuration files 295 | .terraformrc 296 | terraform.rc 297 | 298 | .terraform.lock.hcl 299 | 300 | # Terragrunt 301 | .terragrunt-cache 302 | 303 | # OS X / macOS 304 | .DS_Store 305 | 306 | Artifacts -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.CodeFixes/CodeFixResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Nall.NEST.MigtarionAnalyzer { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class CodeFixResources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal CodeFixResources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Nall.NEST.MigtarionAnalyzer.CodeFixResources", typeof(CodeFixResources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.CodeFixes/CodeFixResources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.CodeFixes/Nall.NEST.MigtarionAnalyzer.CodeFixes.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | false 6 | Nall.NEST.MigtarionAnalyzer 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.CodeFixes/UseElasticsearchNetInsteadOfNestCodeFixProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.Composition; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.CodeAnalysis; 7 | using Microsoft.CodeAnalysis.CodeActions; 8 | using Microsoft.CodeAnalysis.CodeFixes; 9 | using Microsoft.CodeAnalysis.CSharp; 10 | using Microsoft.CodeAnalysis.CSharp.Syntax; 11 | 12 | namespace Nall.NEST.MigtarionAnalyzer 13 | { 14 | [ 15 | ExportCodeFixProvider( 16 | LanguageNames.CSharp, 17 | Name = nameof(UseElasticsearchNetInsteadOfNestCodeFixProvider) 18 | ), 19 | Shared 20 | ] 21 | public class UseElasticsearchNetInsteadOfNestCodeFixProvider : CodeFixProvider 22 | { 23 | public sealed override ImmutableArray FixableDiagnosticIds 24 | { 25 | get { return ImmutableArray.Create(UseElasticsearchNetInsteadOfNest.DiagnosticId); } 26 | } 27 | 28 | public sealed override FixAllProvider GetFixAllProvider() 29 | { 30 | return WellKnownFixAllProviders.BatchFixer; 31 | } 32 | 33 | public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) 34 | { 35 | var root = await context 36 | .Document.GetSyntaxRootAsync(context.CancellationToken) 37 | .ConfigureAwait(false); 38 | 39 | var diagnostic = context.Diagnostics.First(); 40 | var diagnosticSpan = diagnostic.Location.SourceSpan; 41 | 42 | var declaration = root.FindToken(diagnosticSpan.Start) 43 | .Parent.AncestorsAndSelf() 44 | .OfType() 45 | .First(); 46 | 47 | context.RegisterCodeFix( 48 | CodeAction.Create( 49 | title: "Use ElasticsearchClient instead of IElasticClient", 50 | createChangedDocument: c => 51 | ReplaceWithElasticsearchClientAsync(context.Document, declaration, c), 52 | equivalenceKey: nameof(UseElasticsearchNetInsteadOfNestCodeFixProvider) 53 | ), 54 | diagnostic 55 | ); 56 | } 57 | 58 | private async Task ReplaceWithElasticsearchClientAsync( 59 | Document document, 60 | IdentifierNameSyntax identifierName, 61 | CancellationToken cancellationToken 62 | ) 63 | { 64 | var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); 65 | 66 | root = ReplaceUsings(root); 67 | root = ReplaceConnectionSettings(root); 68 | 69 | var nodesToReplace = root.DescendantNodes() 70 | .OfType() 71 | .Where(node => 72 | node.Identifier.ValueText == "IElasticClient" 73 | || node.Identifier.ValueText == "ElasticClient" 74 | ); 75 | 76 | root = root.ReplaceNodes( 77 | nodesToReplace, 78 | (node, _) => 79 | SyntaxFactory.IdentifierName("ElasticsearchClient").WithTriviaFrom(node) 80 | ); 81 | 82 | return document.WithSyntaxRoot(root); 83 | } 84 | 85 | private SyntaxNode ReplaceConnectionSettings(SyntaxNode root) 86 | { 87 | var nodesToReplace = root.DescendantNodes() 88 | .OfType() 89 | .Where(node => node.Identifier.ValueText == "ConnectionSettings"); 90 | 91 | root = root.ReplaceNodes( 92 | nodesToReplace, 93 | (node, _) => 94 | SyntaxFactory.IdentifierName("ElasticsearchClientSettings").WithTriviaFrom(node) 95 | ); 96 | 97 | return root; 98 | } 99 | 100 | private static SyntaxNode ReplaceUsings(SyntaxNode root) 101 | { 102 | // Replace the using directive 103 | var oldUsingDirective = root.DescendantNodes() 104 | .OfType() 105 | .FirstOrDefault(u => u.Name.ToString() == "Nest"); 106 | 107 | if (oldUsingDirective != null) 108 | { 109 | var newUsingDirective = SyntaxFactory 110 | .UsingDirective(SyntaxFactory.ParseName("Elastic.Clients.Elasticsearch")) 111 | .WithTriviaFrom(oldUsingDirective); // Preserve trivia 112 | root = root.ReplaceNode(oldUsingDirective, newUsingDirective); 113 | } 114 | else 115 | { 116 | // If there's no existing Nest using directive, add the new one 117 | var firstUsingDirective = root.DescendantNodes() 118 | .OfType() 119 | .FirstOrDefault(); 120 | if (firstUsingDirective != null) 121 | { 122 | var newUsingDirective = SyntaxFactory.UsingDirective( 123 | SyntaxFactory.ParseName("Elastic.Clients.Elasticsearch") 124 | ); 125 | root = root.InsertNodesBefore(firstUsingDirective, new[] { newUsingDirective }); 126 | } 127 | } 128 | 129 | return root; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Package/Nall.NEST.MigtarionAnalyzer.Package.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | false 6 | true 7 | true 8 | 9 | 10 | 11 | Nall.NEST.MigtarionAnalyzer 12 | 1.2.0.0 13 | Oleksii_Nikiforov 14 | https://github.com/NikiforovAll/elasticsearch-dotnet-playground/LICENSE.md 15 | https://github.com/NikiforovAll/elasticsearch-dotnet-playground 16 | 17 | https://github.com/NikiforovAll/elasticsearch-dotnet-playground 18 | false 19 | 20 | This package helps with migration from Nest to Elastic.Clients.Elasticsearch 21 | Copyright 22 | analyzers 23 | true 24 | true 25 | 26 | $(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Package/tools/install.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | if($project.Object.SupportsPackageDependencyResolution) 4 | { 5 | if($project.Object.SupportsPackageDependencyResolution()) 6 | { 7 | # Do not install analyzers via install.ps1, instead let the project system handle it. 8 | return 9 | } 10 | } 11 | 12 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve 13 | 14 | foreach($analyzersPath in $analyzersPaths) 15 | { 16 | if (Test-Path $analyzersPath) 17 | { 18 | # Install the language agnostic analyzers. 19 | foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) 20 | { 21 | if($project.Object.AnalyzerReferences) 22 | { 23 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) 24 | } 25 | } 26 | } 27 | } 28 | 29 | # $project.Type gives the language name like (C# or VB.NET) 30 | $languageFolder = "" 31 | if($project.Type -eq "C#") 32 | { 33 | $languageFolder = "cs" 34 | } 35 | if($project.Type -eq "VB.NET") 36 | { 37 | $languageFolder = "vb" 38 | } 39 | if($languageFolder -eq "") 40 | { 41 | return 42 | } 43 | 44 | foreach($analyzersPath in $analyzersPaths) 45 | { 46 | # Install language specific analyzers. 47 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder 48 | if (Test-Path $languageAnalyzersPath) 49 | { 50 | foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) 51 | { 52 | if($project.Object.AnalyzerReferences) 53 | { 54 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) 55 | } 56 | } 57 | } 58 | } 59 | # SIG # Begin signature block 60 | # MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor 61 | # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG 62 | # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA/i+qRUHsWzI0s 63 | # FVk99zLgt/HOEQ33uvkFsWtHTHZgf6CCDXYwggX0MIID3KADAgECAhMzAAADTrU8 64 | # esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD 65 | # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy 66 | # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p 67 | # bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw 68 | # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u 69 | # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy 70 | # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB 71 | # AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU 72 | # p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1 73 | # 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm 74 | # WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa 75 | # +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq 76 | # jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE 77 | # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw 78 | # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW 79 | # MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci 80 | # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j 81 | # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG 82 | # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu 83 | # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 84 | # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk 85 | # mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31 86 | # TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2 87 | # kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d 88 | # hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM 89 | # pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh 90 | # JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX 91 | # UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir 92 | # IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8 93 | # 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A 94 | # Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H 95 | # tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq 96 | # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x 97 | # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv 98 | # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 99 | # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG 100 | # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG 101 | # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg 102 | # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC 103 | # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 104 | # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr 105 | # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg 106 | # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy 107 | # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 108 | # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh 109 | # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k 110 | # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB 111 | # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn 112 | # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 113 | # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w 114 | # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o 115 | # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD 116 | # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa 117 | # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny 118 | # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG 119 | # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t 120 | # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV 121 | # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 122 | # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG 123 | # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl 124 | # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb 125 | # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l 126 | # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 127 | # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 128 | # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 129 | # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam 130 | # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa 131 | # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah 132 | # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA 133 | # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt 134 | # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr 135 | # /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw 136 | # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN 137 | # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp 138 | # Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB 139 | # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO 140 | # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIEY4Ow3COroWH11sAEOoStJj 141 | # 1u4sq9rcx0dAx0gyZLHCMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A 142 | # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB 143 | # BQAEggEAFlSLlk17KQp2AwLbr5e4T5zyE44MGZOdNejIip+Mg8co8qho16qdNSvg 144 | # AhvoJYxPJJr70DSCU9BIUoWY7hXoi9S4P08YlAid7BUT5OciIgHlrb8I900LaE+S 145 | # 83rSvfVU1CZCjiSwcgng5DD2VPRo0Lu4G9p2Ky14dyOPwtPvrpsib5s9kewZLdiy 146 | # /KxEDLKX8P+cHat1xH7RaZLDNxweRS6GSomjE2vjOlQHNSW879XR8bSoAt/m4uR1 147 | # WyrAxTGZb4miEYX+I5HsrWvbZLw9NSCJ/crbbap3LIobfQtK5binjY7v4MQp/5Oq 148 | # y4S/4FAfwhWDXfaQfq6YTeOjHRVQbKGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC 149 | # F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq 150 | # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl 151 | # AwQCAQUABCCV3irbWIoYz2Llfx9YQhUNtcP6GOrL7+YTUXQ4y5qzWAIGZNTJrsAW 152 | # GBMyMDIzMDgzMTAwMTI1OC45ODNaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV 153 | # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE 154 | # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l 155 | # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0w 156 | # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg 157 | # ghHqMIIHIDCCBQigAwIBAgITMwAAAc9SNr5xS81IygABAAABzzANBgkqhkiG9w0B 158 | # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE 159 | # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD 160 | # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy 161 | # MTFaFw0yNDAyMDExOTEyMTFaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz 162 | # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv 163 | # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z 164 | # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0wNUUwLUQ5NDcxJTAjBgNV 165 | # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB 166 | # AQUAA4ICDwAwggIKAoICAQC4Pct+15TYyrUje553lzBQodgmd5Bz7WuH8SdHpAoW 167 | # z+01TrHExBSuaMKnxvVMsyYtas5h6aopUGAS5WKVLZAvUtH62TKmAE0JK+i1hafi 168 | # CSXLZPcRexxeRkOqeZefLBzXp0nudMOXUUab333Ss8LkoK4l3LYxm1Ebsr3b2OTo 169 | # 2ebsAoNJ4kSxmVuPM7C+RDhGtVKR/EmHsQ9GcwGmluu54bqiVFd0oAFBbw4txTU1 170 | # mruIGWP/i+sgiNqvdV/wah/QcrKiGlpWiOr9a5aGrJaPSQD2xgEDdPbrSflYxsRM 171 | # dZCJI8vzvOv6BluPcPPGGVLEaU7OszdYjK5f4Z5Su/lPK1eST5PC4RFsVcOiS4L0 172 | # sI4IFZywIdDJHoKgdqWRp6Q5vEDk8kvZz6HWFnYLOlHuqMEYvQLr6OgooYU9z0A5 173 | # cMLHEIHYV1xiaBzx2ERiRY9MUPWohh+TpZWEUZlUm/q9anXVRN0ujejm6OsUVFDs 174 | # sIMszRNCqEotJGwtHHm5xrCKuJkFr8GfwNelFl+XDoHXrQYL9zY7Np+frsTXQpKR 175 | # NnmI1ashcn5EC+wxUt/EZIskWzewEft0/+/0g3+8YtMkUdaQE5+8e7C8UMiXOHkM 176 | # K25jNNQqLCedlJwFIf9ir9SpMc72NR+1j6Uebiz/ZPV74do3jdVvq7DiPFlTb92U 177 | # KwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFDaeKPtp0eTSVdG+gZc5BDkabTg4MB8G 178 | # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG 179 | # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy 180 | # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w 181 | # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy 182 | # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG 183 | # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD 184 | # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQBQgm4pnA0xkd/9uKXJMzdMYyxUfUm/ZusU 185 | # Ba32MEZXQuMGp20pSuX2VW9/tpTMo5bkaJdBVoUyd2DbDsNb1kjr/36ntT0jvL3A 186 | # oWStAFhZBypmpPbx+BPK49ZlejlM4d5epX668tRRGfFip9Til9yKRfXBrXnM/q64 187 | # IinN7zXEQ3FFQhdJMzt8ibXClO7eFA+1HiwZPWysYWPb/ZOFobPEMvXie+GmEbTK 188 | # bhE5tze6RrA9aejjP+v1ouFoD5bMj5Qg+wfZXqe+hfYKpMd8QOnQyez+Nlj1ityn 189 | # OZWfwHVR7dVwV0yLSlPT+yHIO8g+3fWiAwpoO17bDcntSZ7YOBljXrIgad4W4gX+ 190 | # 4tp1eBsc6XWIITPBNzxQDZZRxD4rXzOB6XRlEVJdYZQ8gbXOirg/dNvS2GxcR50Q 191 | # dOXDAumdEHaGNHb6y2InJadCPp2iT5QLC4MnzR+YZno1b8mWpCdOdRs9g21QbbrI 192 | # 06iLk9KD61nx7K5ReSucuS5Z9nbkIBaLUxDesFhr1wmd1ynf0HQ51Swryh7YI7TX 193 | # T0jr81mbvvI9xtoqjFvIhNBsICdCfTR91ylJTH8WtUlpDhEgSqWt3gzNLPTSvXAx 194 | # XTpIM583sZdd+/2YGADMeWmt8PuMce6GsIcLCOF2NiYZ10SXHZS5HRrLrChuzedD 195 | # RisWpIu5uTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI 196 | # hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw 197 | # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x 198 | # MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy 199 | # MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC 200 | # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV 201 | # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp 202 | # bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC 203 | # AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg 204 | # M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF 205 | # dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6 206 | # GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp 207 | # Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu 208 | # yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E 209 | # XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0 210 | # lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q 211 | # GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ 212 | # +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA 213 | # PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw 214 | # EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG 215 | # NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV 216 | # MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj 217 | # cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK 218 | # BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC 219 | # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX 220 | # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v 221 | # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI 222 | # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j 223 | # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG 224 | # 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x 225 | # M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC 226 | # VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449 227 | # xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM 228 | # nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS 229 | # PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d 230 | # Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn 231 | # GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs 232 | # QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL 233 | # jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL 234 | # 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN 235 | # MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp 236 | # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw 237 | # b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn 238 | # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjkyMDAtMDVFMC1EOTQ3MSUwIwYDVQQD 239 | # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQDq 240 | # 8xzVXwLguauAQj1rrJ4/TyEMm6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD 241 | # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy 242 | # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w 243 | # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6JpNsjAiGA8yMDIzMDgzMDIzMjIy 244 | # NloYDzIwMjMwODMxMjMyMjI2WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDomk2y 245 | # AgEAMAcCAQACAi7oMAcCAQACAhLnMAoCBQDom58yAgEAMDYGCisGAQQBhFkKBAIx 246 | # KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI 247 | # hvcNAQELBQADggEBADE16CHIs3WeXYKQG9djja+StAB7gsNJxV9p+CETL3847UL3 248 | # +DIeoj6p5g0FkS8PJK2xc+UbcNx6XJO+WUXEbU9GL4mrOCQjYOM+i3r8FEU+l3Gh 249 | # 6ZG/9ygsIYRXEfDKK4lsbrrUkFQs9nDISHT3f8JZEJJXSsGmwcHWlNC0LC8bv0Jp 250 | # e2Bw2+SNc6SlGD8Vv45r4WFPHhfSioCz4HSsF1He3/2Wku7OH85FKvugBlsca7+F 251 | # bpGsDSL4LO9bv60DxuD+8xBZuyTB8s64ifCGlOXCNpK5VaHND48PhoJbuD0COwlM 252 | # Rn5NlT6T4hhtkPOqNscMlzYHmTOKc5NhWK8PyrIxggQNMIIECQIBATCBkzB8MQsw 253 | # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u 254 | # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy 255 | # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAc9SNr5xS81IygABAAABzzAN 256 | # BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G 257 | # CSqGSIb3DQEJBDEiBCAjGiC3/PscCMBvPZjpZqbYcL2WRZ+Ecf74oiIPQKkjSzCB 258 | # +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EILPpsLqeNS4NuYXE2VJlMuvQeWVA 259 | # 80ZDFhpOPjSzhPa/MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh 260 | # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD 261 | # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw 262 | # MTACEzMAAAHPUja+cUvNSMoAAQAAAc8wIgQgWdbLA3Co0zf3nE5086HU0tdn0vK7 263 | # yXyU9aAFpszvWFIwDQYJKoZIhvcNAQELBQAEggIANrHN06oaP0N1lUSxxoJteJgI 264 | # fQjN82xV5VSE0cwNCBy05fg1VydPERF59+MIwIlJHhikSo3YOj5tt3AohC78U46P 265 | # 2IdrLHKlA3rjiLUGwQvGUKJUSsEH4uueA1mmh9jwUBJhY3NjTcMQaQqp/oxZTaya 266 | # l7NqbubBIZvsDD126SUr8jtVWtZZzw8pnWCFb4Rijii4fY1UiQzfQLFwqQuid6tE 267 | # I0AaY3IoTlp7U9K2wfAPWcP1G7n3qv+990GEiQlGJlCfIJSSJQodzL2QZF5HCn/K 268 | # SfgkRPn3y/Aax8683mWCT8zricYzO3MZ9j0T7tAcqOiWb7PFCsk5Va44lq4Gv0u+ 269 | # +60FYCAA/Qn6eMuNkqIpBeIK0+NYUcMSwPdY/riKXdgkVLwChEJC5WWznD/Iqsil 270 | # Jj9XYailhXzYx7Pa2MLays62LPwCnUxRQBTD9/rL3XQMj69iA4lisb51dKAtrqAU 271 | # 53aRXFn+twYYFTAUQ/oNK14nZEQE5H53xAfhphMokJOu+CnQQKeCMeYlex6Q4zfw 272 | # TQxP/xXZ+QW2cSZTwh1d5iE0XMhKCZxhxIOF/rRA+75L5GUz60reRZPeH/7USYZL 273 | # VPc0+kxIdSbNFJAhAp0u59wSMQdBofor3+HfDfmxmoSjfCSH4TvOkNIulX1PPJPX 274 | # UB7H4n7XHWJPkUU0cUw= 275 | # SIG # End signature block 276 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Nall.NEST.MigtarionAnalyzer.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | 6 | true 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/UseElasticsearchNetInsteadOfNestTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Analyzer.Test; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp.Testing; 5 | using Microsoft.CodeAnalysis.Testing; 6 | using Microsoft.CodeAnalysis.Testing.Verifiers; 7 | using Microsoft.VisualStudio.TestTools.UnitTesting; 8 | using Nest; 9 | 10 | namespace Nall.NEST.MigtarionAnalyzer.Test; 11 | 12 | [TestClass] 13 | public class UseElasticsearchNetInsteadOfNestTests 14 | { 15 | [TestMethod] 16 | public async Task TestNestUsage() 17 | { 18 | var test = 19 | @" 20 | using Nest; 21 | public class ElasticsearchService 22 | { 23 | private readonly {|#0:IElasticClient|} _client; 24 | 25 | public ElasticsearchService({|#1:IElasticClient|} client) 26 | { 27 | _client = client; 28 | } 29 | }"; 30 | 31 | var expected1 = VerifyCS 32 | .Diagnostic("ELS001") 33 | .WithLocation(0) 34 | .WithArguments("IElasticClient"); 35 | 36 | var expected2 = VerifyCS 37 | .Diagnostic("ELS001") 38 | .WithLocation(1) 39 | .WithArguments("IElasticClient"); 40 | 41 | await VerifyCS.VerifyAnalyzerAsync(test, expected1, expected2); 42 | } 43 | 44 | [TestMethod] 45 | public async Task TestNestUsageAsClass() 46 | { 47 | var test = 48 | @" 49 | using Nest; 50 | public class ElasticsearchService 51 | { 52 | private readonly {|#0:ElasticClient|} _client; 53 | 54 | public ElasticsearchService({|#1:ElasticClient|} client) 55 | { 56 | _client = client; 57 | } 58 | }"; 59 | 60 | var expected1 = VerifyCS 61 | .Diagnostic("ELS001") 62 | .WithLocation(0) 63 | .WithArguments("ElasticClient"); 64 | 65 | var expected2 = VerifyCS 66 | .Diagnostic("ELS001") 67 | .WithLocation(1) 68 | .WithArguments("ElasticClient"); 69 | 70 | await VerifyCS.VerifyAnalyzerAsync(test, expected1, expected2); 71 | } 72 | 73 | //Diagnostic and CodeFix both triggered and checked for 74 | [TestMethod] 75 | public async Task TestCodeFix() 76 | { 77 | var test = """ 78 | using Nest; 79 | 80 | public class ElasticsearchService 81 | { 82 | private readonly {|#0:IElasticClient|} _client; 83 | } 84 | """; 85 | 86 | var fixtest = """ 87 | using Elastic.Clients.Elasticsearch; 88 | 89 | public class ElasticsearchService 90 | { 91 | private readonly ElasticsearchClient _client; 92 | } 93 | """; 94 | 95 | var expected = VerifyCS 96 | .Diagnostic("ELS001") 97 | .WithLocation(0) 98 | .WithArguments("IElasticClient"); 99 | 100 | await VerifyCS.VerifyCodeFixAsync(test, [expected], fixtest); 101 | } 102 | 103 | [TestMethod] 104 | public async Task TestCodeFixPreservesWhitespace() 105 | { 106 | var test = """ 107 | using Nest; 108 | 109 | public class ElasticsearchService 110 | { 111 | private readonly {|#0:IElasticClient|} _client; 112 | 113 | public ElasticsearchService( {|#1:IElasticClient|} client) 114 | { 115 | _client = client; 116 | } 117 | } 118 | """; 119 | 120 | var fixtest = """ 121 | using Elastic.Clients.Elasticsearch; 122 | 123 | public class ElasticsearchService 124 | { 125 | private readonly ElasticsearchClient _client; 126 | 127 | public ElasticsearchService( ElasticsearchClient client) 128 | { 129 | _client = client; 130 | } 131 | } 132 | """; 133 | 134 | var expected1 = VerifyCS 135 | .Diagnostic("ELS001") 136 | .WithLocation(0) 137 | .WithArguments("IElasticClient"); 138 | 139 | var expected2 = VerifyCS 140 | .Diagnostic("ELS001") 141 | .WithLocation(1) 142 | .WithArguments("IElasticClient"); 143 | 144 | await VerifyCS.VerifyCodeFixAsync(test, [expected1, expected2], fixtest); 145 | } 146 | 147 | [TestMethod] 148 | public async Task TestCodeFixWithSettings() 149 | { 150 | var test = """ 151 | using Nest; 152 | 153 | public class ElasticsearchService 154 | { 155 | private readonly {|#0:ElasticClient|} _client; 156 | 157 | public ElasticsearchService() 158 | { 159 | var settings = new ConnectionSettings(new Uri("https://elastic:elastic@127.0.0.1:9200/")); 160 | _client = new {|#1:ElasticClient|}(settings); 161 | } 162 | } 163 | """; 164 | 165 | var fixtest = """ 166 | using Elastic.Clients.Elasticsearch; 167 | 168 | public class ElasticsearchService 169 | { 170 | private readonly ElasticsearchClient _client; 171 | 172 | public ElasticsearchService() 173 | { 174 | var settings = new ElasticsearchClientSettings(new Uri("https://elastic:elastic@127.0.0.1:9200/")); 175 | _client = new ElasticsearchClient(settings); 176 | } 177 | } 178 | """; 179 | 180 | var expected1 = VerifyCS 181 | .Diagnostic("ELS001") 182 | .WithLocation(0) 183 | .WithArguments("ElasticClient"); 184 | 185 | var expected2 = VerifyCS 186 | .Diagnostic("ELS001") 187 | .WithLocation(1) 188 | .WithArguments("ElasticClient"); 189 | 190 | await VerifyCS.VerifyCodeFixAsync(test, [expected1, expected2], fixtest); 191 | } 192 | 193 | [TestMethod] 194 | public async Task SameNameButNotFromNamespaceShouldNotAddDiagnostic() 195 | { 196 | var test = """ 197 | public class ElasticClient 198 | { 199 | } 200 | public class IElasticClient 201 | { 202 | } 203 | 204 | public class MyService{ 205 | private readonly ElasticClient _client1; 206 | private readonly IElasticClient _client2; 207 | } 208 | """; 209 | 210 | await VerifyCS.VerifyAnalyzerAsync(test, []); 211 | } 212 | } 213 | 214 | public static class VerifyCS 215 | { 216 | private static readonly MetadataReference s_nestReference = MetadataReference.CreateFromFile( 217 | typeof(IElasticClient).Assembly.Location 218 | ); 219 | private static readonly MetadataReference s_elasticReference = MetadataReference.CreateFromFile( 220 | typeof(Elastic.Clients.Elasticsearch.ElasticsearchClient).Assembly.Location 221 | ); 222 | 223 | public static DiagnosticResult Diagnostic(string diagnosticId) => 224 | CSharpAnalyzerVerifier.Diagnostic( 225 | diagnosticId 226 | ); 227 | 228 | public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) 229 | { 230 | var test = new Test { TestCode = source }; 231 | test.TestState.AdditionalReferences.Add(s_nestReference); 232 | test.TestState.AdditionalReferences.Add(s_elasticReference); 233 | test.ExpectedDiagnostics.AddRange(expected); 234 | return test.RunAsync(); 235 | } 236 | 237 | public static Task VerifyCodeFixAsync( 238 | string source, 239 | DiagnosticResult[] expected, 240 | string fixedSource 241 | ) 242 | { 243 | var test = new Test { TestCode = source, FixedCode = fixedSource }; 244 | test.TestState.AdditionalReferences.Add(s_nestReference); 245 | test.TestState.AdditionalReferences.Add(s_elasticReference); 246 | test.ExpectedDiagnostics.AddRange(expected); 247 | // we need this becaues of 248 | // error CS1705: Assembly 'Elastic.Clients.Elasticsearch' with identity 'Elastic.Clients.Elasticsearch, Version=8.0.0.0, Culture=neutral, PublicKeyToken=96c599bbe3e70f5d' uses 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' which has a higher version than referenced assembly 'System.Runtime' with identity 'System.Runtime, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' 249 | test.CompilerDiagnostics = CompilerDiagnostics.None; 250 | return test.RunAsync(); 251 | } 252 | 253 | private class Test 254 | : CSharpCodeFixTest< 255 | UseElasticsearchNetInsteadOfNest, 256 | UseElasticsearchNetInsteadOfNestCodeFixProvider, 257 | MSTestVerifier 258 | > 259 | { 260 | public Test() 261 | { 262 | SolutionTransforms.Add( 263 | (solution, projectId) => 264 | { 265 | var compilationOptions = solution.GetProject(projectId).CompilationOptions; 266 | compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( 267 | compilationOptions.SpecificDiagnosticOptions.SetItems( 268 | CSharpVerifierHelper.NullableWarnings 269 | ) 270 | ); 271 | solution = solution.WithProjectCompilationOptions( 272 | projectId, 273 | compilationOptions 274 | ); 275 | 276 | return solution; 277 | } 278 | ); 279 | } 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/CSharpAnalyzerVerifier`1+Test.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp.Testing; 2 | using Microsoft.CodeAnalysis.Diagnostics; 3 | using Microsoft.CodeAnalysis.Testing.Verifiers; 4 | 5 | namespace Analyzer.Test; 6 | 7 | public static partial class CSharpAnalyzerVerifier 8 | where TAnalyzer : DiagnosticAnalyzer, new() 9 | { 10 | public class Test : CSharpAnalyzerTest 11 | { 12 | public Test() 13 | { 14 | SolutionTransforms.Add( 15 | (solution, projectId) => 16 | { 17 | var compilationOptions = solution.GetProject(projectId).CompilationOptions; 18 | compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( 19 | compilationOptions.SpecificDiagnosticOptions.SetItems( 20 | CSharpVerifierHelper.NullableWarnings 21 | ) 22 | ); 23 | solution = solution.WithProjectCompilationOptions( 24 | projectId, 25 | compilationOptions 26 | ); 27 | 28 | return solution; 29 | } 30 | ); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/CSharpAnalyzerVerifier`1.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp.Testing; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | using Microsoft.CodeAnalysis.Testing; 7 | using Microsoft.CodeAnalysis.Testing.Verifiers; 8 | 9 | namespace Analyzer.Test; 10 | 11 | public static partial class CSharpAnalyzerVerifier 12 | where TAnalyzer : DiagnosticAnalyzer, new() 13 | { 14 | /// 15 | public static DiagnosticResult Diagnostic() => 16 | CSharpAnalyzerVerifier.Diagnostic(); 17 | 18 | /// 19 | public static DiagnosticResult Diagnostic(string diagnosticId) => 20 | CSharpAnalyzerVerifier.Diagnostic(diagnosticId); 21 | 22 | /// 23 | public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) => 24 | CSharpAnalyzerVerifier.Diagnostic(descriptor); 25 | 26 | /// 27 | public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) 28 | { 29 | var test = new Test { TestCode = source, }; 30 | 31 | test.ExpectedDiagnostics.AddRange(expected); 32 | await test.RunAsync(CancellationToken.None); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/CSharpCodeFixVerifier`2+Test.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CodeFixes; 2 | using Microsoft.CodeAnalysis.CSharp.Testing; 3 | using Microsoft.CodeAnalysis.Diagnostics; 4 | using Microsoft.CodeAnalysis.Testing.Verifiers; 5 | 6 | namespace Analyzer.Test; 7 | 8 | public static partial class CSharpCodeFixVerifier 9 | where TAnalyzer : DiagnosticAnalyzer, new() 10 | where TCodeFix : CodeFixProvider, new() 11 | { 12 | public class Test : CSharpCodeFixTest 13 | { 14 | public Test() 15 | { 16 | SolutionTransforms.Add( 17 | (solution, projectId) => 18 | { 19 | var compilationOptions = solution.GetProject(projectId).CompilationOptions; 20 | compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( 21 | compilationOptions.SpecificDiagnosticOptions.SetItems( 22 | CSharpVerifierHelper.NullableWarnings 23 | ) 24 | ); 25 | solution = solution.WithProjectCompilationOptions( 26 | projectId, 27 | compilationOptions 28 | ); 29 | 30 | return solution; 31 | } 32 | ); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/CSharpCodeFixVerifier`2.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CodeFixes; 5 | using Microsoft.CodeAnalysis.CSharp.Testing; 6 | using Microsoft.CodeAnalysis.Diagnostics; 7 | using Microsoft.CodeAnalysis.Testing; 8 | using Microsoft.CodeAnalysis.Testing.Verifiers; 9 | 10 | namespace Analyzer.Test; 11 | 12 | public static partial class CSharpCodeFixVerifier 13 | where TAnalyzer : DiagnosticAnalyzer, new() 14 | where TCodeFix : CodeFixProvider, new() 15 | { 16 | /// 17 | public static DiagnosticResult Diagnostic() => 18 | CSharpCodeFixVerifier.Diagnostic(); 19 | 20 | /// 21 | public static DiagnosticResult Diagnostic(string diagnosticId) => 22 | CSharpCodeFixVerifier.Diagnostic(diagnosticId); 23 | 24 | /// 25 | public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) => 26 | CSharpCodeFixVerifier.Diagnostic(descriptor); 27 | 28 | /// 29 | public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) 30 | { 31 | var test = new Test { TestCode = source, }; 32 | 33 | test.ExpectedDiagnostics.AddRange(expected); 34 | await test.RunAsync(CancellationToken.None); 35 | } 36 | 37 | /// 38 | public static async Task VerifyCodeFixAsync(string source, string fixedSource) => 39 | await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); 40 | 41 | /// 42 | public static async Task VerifyCodeFixAsync( 43 | string source, 44 | DiagnosticResult expected, 45 | string fixedSource 46 | ) => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource); 47 | 48 | /// 49 | public static async Task VerifyCodeFixAsync( 50 | string source, 51 | DiagnosticResult[] expected, 52 | string fixedSource 53 | ) 54 | { 55 | var test = new Test { TestCode = source, FixedCode = fixedSource, }; 56 | 57 | test.ExpectedDiagnostics.AddRange(expected); 58 | await test.RunAsync(CancellationToken.None); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/CSharpCodeRefactoringVerifier`1+Test.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CodeRefactorings; 2 | using Microsoft.CodeAnalysis.CSharp.Testing; 3 | using Microsoft.CodeAnalysis.Testing.Verifiers; 4 | 5 | namespace Analyzer.Test; 6 | 7 | public static partial class CSharpCodeRefactoringVerifier 8 | where TCodeRefactoring : CodeRefactoringProvider, new() 9 | { 10 | public class Test : CSharpCodeRefactoringTest 11 | { 12 | public Test() 13 | { 14 | SolutionTransforms.Add( 15 | (solution, projectId) => 16 | { 17 | var compilationOptions = solution.GetProject(projectId).CompilationOptions; 18 | compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( 19 | compilationOptions.SpecificDiagnosticOptions.SetItems( 20 | CSharpVerifierHelper.NullableWarnings 21 | ) 22 | ); 23 | solution = solution.WithProjectCompilationOptions( 24 | projectId, 25 | compilationOptions 26 | ); 27 | 28 | return solution; 29 | } 30 | ); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/CSharpCodeRefactoringVerifier`1.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Microsoft.CodeAnalysis.CodeRefactorings; 4 | using Microsoft.CodeAnalysis.Testing; 5 | 6 | namespace Analyzer.Test; 7 | 8 | public static partial class CSharpCodeRefactoringVerifier 9 | where TCodeRefactoring : CodeRefactoringProvider, new() 10 | { 11 | /// 12 | public static async Task VerifyRefactoringAsync(string source, string fixedSource) 13 | { 14 | await VerifyRefactoringAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); 15 | } 16 | 17 | /// 18 | public static async Task VerifyRefactoringAsync( 19 | string source, 20 | DiagnosticResult expected, 21 | string fixedSource 22 | ) 23 | { 24 | await VerifyRefactoringAsync(source, new[] { expected }, fixedSource); 25 | } 26 | 27 | /// 28 | public static async Task VerifyRefactoringAsync( 29 | string source, 30 | DiagnosticResult[] expected, 31 | string fixedSource 32 | ) 33 | { 34 | var test = new Test { TestCode = source, FixedCode = fixedSource, }; 35 | 36 | test.ExpectedDiagnostics.AddRange(expected); 37 | await test.RunAsync(CancellationToken.None); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/CSharpVerifierHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Immutable; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | 6 | namespace Analyzer.Test; 7 | 8 | internal static class CSharpVerifierHelper 9 | { 10 | /// 11 | /// By default, the compiler reports diagnostics for nullable reference types at 12 | /// , and the analyzer test framework defaults to only validating 13 | /// diagnostics at . This map contains all compiler diagnostic IDs 14 | /// related to nullability mapped to , which is then used to enable all 15 | /// of these warnings for default validation during analyzer and code fix tests. 16 | /// 17 | internal static ImmutableDictionary NullableWarnings { get; } = 18 | GetNullableWarningsFromCompiler(); 19 | 20 | private static ImmutableDictionary GetNullableWarningsFromCompiler() 21 | { 22 | string[] args = { "/warnaserror:nullable" }; 23 | var commandLineArguments = CSharpCommandLineParser.Default.Parse( 24 | args, 25 | baseDirectory: Environment.CurrentDirectory, 26 | sdkDirectory: Environment.CurrentDirectory 27 | ); 28 | var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; 29 | 30 | // Workaround for https://github.com/dotnet/roslyn/issues/41610 31 | nullableWarnings = nullableWarnings 32 | .SetItem("CS8632", ReportDiagnostic.Error) 33 | .SetItem("CS8669", ReportDiagnostic.Error); 34 | 35 | return nullableWarnings; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/VisualBasicAnalyzerVerifier`1+Test.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.Diagnostics; 2 | using Microsoft.CodeAnalysis.Testing.Verifiers; 3 | using Microsoft.CodeAnalysis.VisualBasic.Testing; 4 | 5 | namespace Analyzer.Test; 6 | 7 | public static partial class VisualBasicAnalyzerVerifier 8 | where TAnalyzer : DiagnosticAnalyzer, new() 9 | { 10 | public class Test : VisualBasicAnalyzerTest 11 | { 12 | public Test() { } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/VisualBasicAnalyzerVerifier`1.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.Diagnostics; 5 | using Microsoft.CodeAnalysis.Testing; 6 | using Microsoft.CodeAnalysis.Testing.Verifiers; 7 | using Microsoft.CodeAnalysis.VisualBasic.Testing; 8 | 9 | namespace Analyzer.Test; 10 | 11 | public static partial class VisualBasicAnalyzerVerifier 12 | where TAnalyzer : DiagnosticAnalyzer, new() 13 | { 14 | /// 15 | public static DiagnosticResult Diagnostic() => 16 | VisualBasicAnalyzerVerifier.Diagnostic(); 17 | 18 | /// 19 | public static DiagnosticResult Diagnostic(string diagnosticId) => 20 | VisualBasicAnalyzerVerifier.Diagnostic(diagnosticId); 21 | 22 | /// 23 | public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) => 24 | VisualBasicAnalyzerVerifier.Diagnostic(descriptor); 25 | 26 | /// 27 | public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) 28 | { 29 | var test = new Test { TestCode = source, }; 30 | 31 | test.ExpectedDiagnostics.AddRange(expected); 32 | await test.RunAsync(CancellationToken.None); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/VisualBasicCodeFixVerifier`2+Test.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CodeFixes; 2 | using Microsoft.CodeAnalysis.Diagnostics; 3 | using Microsoft.CodeAnalysis.Testing.Verifiers; 4 | using Microsoft.CodeAnalysis.VisualBasic.Testing; 5 | 6 | namespace Analyzer.Test; 7 | 8 | public static partial class VisualBasicCodeFixVerifier 9 | where TAnalyzer : DiagnosticAnalyzer, new() 10 | where TCodeFix : CodeFixProvider, new() 11 | { 12 | public class Test : VisualBasicCodeFixTest { } 13 | } 14 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/VisualBasicCodeFixVerifier`2.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CodeFixes; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | using Microsoft.CodeAnalysis.Testing; 7 | using Microsoft.CodeAnalysis.Testing.Verifiers; 8 | using Microsoft.CodeAnalysis.VisualBasic.Testing; 9 | 10 | namespace Analyzer.Test; 11 | 12 | public static partial class VisualBasicCodeFixVerifier 13 | where TAnalyzer : DiagnosticAnalyzer, new() 14 | where TCodeFix : CodeFixProvider, new() 15 | { 16 | /// 17 | public static DiagnosticResult Diagnostic() => 18 | VisualBasicCodeFixVerifier.Diagnostic(); 19 | 20 | /// 21 | public static DiagnosticResult Diagnostic(string diagnosticId) => 22 | VisualBasicCodeFixVerifier.Diagnostic(diagnosticId); 23 | 24 | /// 25 | public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) => 26 | VisualBasicCodeFixVerifier.Diagnostic(descriptor); 27 | 28 | /// 29 | public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) 30 | { 31 | var test = new Test { TestCode = source, }; 32 | 33 | test.ExpectedDiagnostics.AddRange(expected); 34 | await test.RunAsync(CancellationToken.None); 35 | } 36 | 37 | /// 38 | public static async Task VerifyCodeFixAsync(string source, string fixedSource) => 39 | await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); 40 | 41 | /// 42 | public static async Task VerifyCodeFixAsync( 43 | string source, 44 | DiagnosticResult expected, 45 | string fixedSource 46 | ) => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource); 47 | 48 | /// 49 | public static async Task VerifyCodeFixAsync( 50 | string source, 51 | DiagnosticResult[] expected, 52 | string fixedSource 53 | ) 54 | { 55 | var test = new Test { TestCode = source, FixedCode = fixedSource, }; 56 | 57 | test.ExpectedDiagnostics.AddRange(expected); 58 | await test.RunAsync(CancellationToken.None); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/VisualBasicCodeRefactoringVerifier`1+Test.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CodeRefactorings; 2 | using Microsoft.CodeAnalysis.Testing.Verifiers; 3 | using Microsoft.CodeAnalysis.VisualBasic.Testing; 4 | 5 | namespace Analyzer.Test; 6 | 7 | public static partial class VisualBasicCodeRefactoringVerifier 8 | where TCodeRefactoring : CodeRefactoringProvider, new() 9 | { 10 | public class Test : VisualBasicCodeRefactoringTest { } 11 | } 12 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Test/Verifiers/VisualBasicCodeRefactoringVerifier`1.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Microsoft.CodeAnalysis.CodeRefactorings; 4 | using Microsoft.CodeAnalysis.Testing; 5 | 6 | namespace Analyzer.Test; 7 | 8 | public static partial class VisualBasicCodeRefactoringVerifier 9 | where TCodeRefactoring : CodeRefactoringProvider, new() 10 | { 11 | /// 12 | public static async Task VerifyRefactoringAsync(string source, string fixedSource) 13 | { 14 | await VerifyRefactoringAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); 15 | } 16 | 17 | /// 18 | public static async Task VerifyRefactoringAsync( 19 | string source, 20 | DiagnosticResult expected, 21 | string fixedSource 22 | ) 23 | { 24 | await VerifyRefactoringAsync(source, new[] { expected }, fixedSource); 25 | } 26 | 27 | /// 28 | public static async Task VerifyRefactoringAsync( 29 | string source, 30 | DiagnosticResult[] expected, 31 | string fixedSource 32 | ) 33 | { 34 | var test = new Test { TestCode = source, FixedCode = fixedSource, }; 35 | 36 | test.ExpectedDiagnostics.AddRange(expected); 37 | await test.RunAsync(CancellationToken.None); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Vsix/Nall.NEST.MigtarionAnalyzer.Vsix.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | net472 7 | Nall.NEST.MigtarionAnalyzer.Vsix 8 | Nall.NEST.MigtarionAnalyzer.Vsix 9 | 10 | 11 | 12 | false 13 | false 14 | false 15 | false 16 | false 17 | false 18 | Roslyn 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Program 27 | $(DevEnvDir)devenv.exe 28 | /rootsuffix $(VSSDKTargetPlatformRegRootSuffix) 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer.Vsix/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nall.NEST.MigtarionAnalyzer 6 | This package helps with migration from Nest to Elastic.Clients.Elasticsearch. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer/Nall.NEST.MigtarionAnalyzer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | false 6 | 7 | 8 | *$(MSBuildProjectFile)* 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Nall.NEST.MigtarionAnalyzer { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Nall.NEST.MigtarionAnalyzer.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /analyzer/Nall.NEST.MigtarionAnalyzer/UseElasticsearchNetInsteadOfNest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; 5 | using Microsoft.CodeAnalysis.Diagnostics; 6 | 7 | namespace Nall.NEST.MigtarionAnalyzer 8 | { 9 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 10 | public class UseElasticsearchNetInsteadOfNest : DiagnosticAnalyzer 11 | { 12 | public const string DiagnosticId = "ELS001"; 13 | 14 | private static readonly LocalizableString s_title = "Use ElasticsearchClient"; 15 | private static readonly LocalizableString s_messageFormat = 16 | "Use ElasticsearchClient instead of IElasticClient"; 17 | private static readonly LocalizableString s_description = 18 | "NEST is deprecated. Use the official Elasticsearch.Net client instead."; 19 | private const string Category = "Usage"; 20 | 21 | private static readonly DiagnosticDescriptor s_rule = new DiagnosticDescriptor( 22 | DiagnosticId, 23 | s_title, 24 | s_messageFormat, 25 | Category, 26 | DiagnosticSeverity.Warning, 27 | isEnabledByDefault: true, 28 | description: s_description 29 | ); 30 | 31 | public override ImmutableArray SupportedDiagnostics 32 | { 33 | get { return ImmutableArray.Create(s_rule); } 34 | } 35 | 36 | public override void Initialize(AnalysisContext context) 37 | { 38 | context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); 39 | context.EnableConcurrentExecution(); 40 | 41 | // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information 42 | context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.IdentifierName); 43 | } 44 | 45 | private static void AnalyzeNode(SyntaxNodeAnalysisContext context) 46 | { 47 | var identifierNode = (IdentifierNameSyntax)context.Node; 48 | 49 | var indetifierText = identifierNode.Identifier.Text; 50 | 51 | if (indetifierText != "IElasticClient" && indetifierText != "ElasticClient") 52 | { 53 | return; 54 | } 55 | 56 | var symbolInfo = context.SemanticModel.GetSymbolInfo(identifierNode); 57 | if (symbolInfo.Symbol == null) 58 | { 59 | return; 60 | } 61 | 62 | var containingNamespace = symbolInfo.Symbol.ContainingNamespace; 63 | if (containingNamespace == null || containingNamespace.ToString() != "Nest") 64 | { 65 | return; 66 | } 67 | 68 | var diagnostic = Diagnostic.Create(s_rule, identifierNode.GetLocation()); 69 | context.ReportDiagnostic(diagnostic); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /analyzer/Playground/Playground.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | runtime; build; native; contentfiles; analyzers; buildtransitive 13 | all 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /analyzer/Playground/Playground.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Playground", "Playground.csproj", "{CF7AE166-9099-4DCE-995B-85842C623790}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(SolutionProperties) = preSolution 14 | HideSolutionNode = FALSE 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {CF7AE166-9099-4DCE-995B-85842C623790}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {CF7AE166-9099-4DCE-995B-85842C623790}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {CF7AE166-9099-4DCE-995B-85842C623790}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {CF7AE166-9099-4DCE-995B-85842C623790}.Release|Any CPU.Build.0 = Release|Any CPU 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /analyzer/Playground/Program.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS8321 // Local function is declared but never used 2 | 3 | using System.Text.Json; 4 | using Elastic.Transport; 5 | using Nest; 6 | 7 | // ElasticClient -> ElasticsearchClient 8 | // IElasticClient -> ElasticsearchClient 9 | // ConnectionSettings -> ElasticsearchClientSettings 10 | 11 | var settings = new ConnectionSettings(new Uri("https://elastic:elastic@127.0.0.1:9200/")) 12 | .DisableDirectStreaming() 13 | .ServerCertificateValidationCallback(CertificateValidations.AllowAll); 14 | 15 | IElasticClient client = new ElasticClient(settings); 16 | 17 | var nodeInfo = await client.Nodes.InfoAsync(); 18 | 19 | Console.WriteLine( 20 | JsonSerializer.Serialize(nodeInfo, new JsonSerializerOptions { WriteIndented = true }) 21 | ); 22 | 23 | static IElasticClient Create1(ConnectionSettings settings) 24 | { 25 | return new ElasticClient(settings); 26 | } 27 | static ElasticClient Create2(ConnectionSettings settings) 28 | { 29 | return new ElasticClient(settings); 30 | } 31 | -------------------------------------------------------------------------------- /analyzer/README.md: -------------------------------------------------------------------------------- 1 | # Analyzer [![Build](https://github.com/NikiforovAll/elasticsearch-dotnet-playground/actions/workflows/build.yaml/badge.svg)](https://github.com/NikiforovAll/elasticsearch-dotnet-playground/actions/workflows/build.yaml) 2 | 3 | This repository contains [Nall.NEST.MigtarionAnalyzer](https://www.nuget.org/packages/Nall.NEST.MigtarionAnalyzer) analyzer that helps with migration from `Nest` to `Elastic.Clients.Elasticsearch`. 4 | 5 | ```bash 6 | dotnet add package Nall.NEST.MigtarionAnalyzer --version 1.1.0 7 | ``` 8 | 9 | Rules: `ELS001` 10 | 11 | 12 | ```bash 13 | dotnet format analyzers ./path/to/folder \ 14 | --verify-no-changes \ 15 | --diagnostics ELS001 \ 16 | --severity info 17 | ``` 18 | -------------------------------------------------------------------------------- /analyzer/analyzer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nall.NEST.MigtarionAnalyzer", "Nall.NEST.MigtarionAnalyzer\Nall.NEST.MigtarionAnalyzer.csproj", "{5B3F3DB1-20B9-4834-83CC-17334839193B}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nall.NEST.MigtarionAnalyzer.CodeFixes", "Nall.NEST.MigtarionAnalyzer.CodeFixes\Nall.NEST.MigtarionAnalyzer.CodeFixes.csproj", "{480B9220-B0B2-4517-9980-F2C1B19B099C}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nall.NEST.MigtarionAnalyzer.Package", "Nall.NEST.MigtarionAnalyzer.Package\Nall.NEST.MigtarionAnalyzer.Package.csproj", "{2F963F87-9C92-4C23-8802-B5584B565E0C}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nall.NEST.MigtarionAnalyzer.Test", "Nall.NEST.MigtarionAnalyzer.Test\Nall.NEST.MigtarionAnalyzer.Test.csproj", "{C4F70E21-C467-49CA-93A9-4FE0DEFC6F21}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nall.NEST.MigtarionAnalyzer.Vsix", "Nall.NEST.MigtarionAnalyzer.Vsix\Nall.NEST.MigtarionAnalyzer.Vsix.csproj", "{60AA48E2-2F4D-4CD9-9CCC-F4B9332B3865}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {5B3F3DB1-20B9-4834-83CC-17334839193B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {5B3F3DB1-20B9-4834-83CC-17334839193B}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {5B3F3DB1-20B9-4834-83CC-17334839193B}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {5B3F3DB1-20B9-4834-83CC-17334839193B}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {480B9220-B0B2-4517-9980-F2C1B19B099C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {480B9220-B0B2-4517-9980-F2C1B19B099C}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {480B9220-B0B2-4517-9980-F2C1B19B099C}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {480B9220-B0B2-4517-9980-F2C1B19B099C}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {2F963F87-9C92-4C23-8802-B5584B565E0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {2F963F87-9C92-4C23-8802-B5584B565E0C}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {2F963F87-9C92-4C23-8802-B5584B565E0C}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {2F963F87-9C92-4C23-8802-B5584B565E0C}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {C4F70E21-C467-49CA-93A9-4FE0DEFC6F21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {C4F70E21-C467-49CA-93A9-4FE0DEFC6F21}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {C4F70E21-C467-49CA-93A9-4FE0DEFC6F21}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {C4F70E21-C467-49CA-93A9-4FE0DEFC6F21}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {60AA48E2-2F4D-4CD9-9CCC-F4B9332B3865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {60AA48E2-2F4D-4CD9-9CCC-F4B9332B3865}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {60AA48E2-2F4D-4CD9-9CCC-F4B9332B3865}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {60AA48E2-2F4D-4CD9-9CCC-F4B9332B3865}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /analyzer/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.401", 4 | "rollForward": "latestMajor", 5 | "allowPrerelease": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /assets/elastic-search-playground-demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NikiforovAll/elasticsearch-dotnet-playground/290e456221f0997dd14c137f4fad67bc8bb44dba/assets/elastic-search-playground-demo.mp4 -------------------------------------------------------------------------------- /assets/elastic-search-playground-demo2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NikiforovAll/elasticsearch-dotnet-playground/290e456221f0997dd14c137f4fad67bc8bb44dba/assets/elastic-search-playground-demo2.mp4 -------------------------------------------------------------------------------- /assets/setup-elastic-infra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NikiforovAll/elasticsearch-dotnet-playground/290e456221f0997dd14c137f4fad67bc8bb44dba/assets/setup-elastic-infra.png -------------------------------------------------------------------------------- /src/_infra/azure-infra.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Deploy Azure Resources\n", 8 | "\n", 9 | "https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/create-resource?pivots=cli" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": { 16 | "dotnet_interactive": { 17 | "language": "pwsh" 18 | }, 19 | "polyglot_notebook": { 20 | "kernelName": "pwsh" 21 | } 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "az account show\n", 26 | "\n", 27 | "$location = \"eastus\"\n", 28 | "$resourceGroup = \"rg-elasticsearch-playground\"\n", 29 | "$aiResourceName = \"ai-elasticsearch-playground\"\n" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": { 36 | "dotnet_interactive": { 37 | "language": "pwsh" 38 | }, 39 | "polyglot_notebook": { 40 | "kernelName": "pwsh" 41 | } 42 | }, 43 | "outputs": [], 44 | "source": [ 45 | "az group create `\n", 46 | " --name rg-elasticsearch-playground `\n", 47 | " --location $location" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": null, 53 | "metadata": { 54 | "dotnet_interactive": { 55 | "language": "pwsh" 56 | }, 57 | "polyglot_notebook": { 58 | "kernelName": "pwsh" 59 | } 60 | }, 61 | "outputs": [], 62 | "source": [ 63 | "az group show --name rg-elasticsearch-playground" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": { 70 | "dotnet_interactive": { 71 | "language": "pwsh" 72 | }, 73 | "polyglot_notebook": { 74 | "kernelName": "pwsh" 75 | } 76 | }, 77 | "outputs": [], 78 | "source": [ 79 | "az cognitiveservices account create `\n", 80 | " --name $aiResourceName `\n", 81 | " --resource-group $resourceGroup `\n", 82 | " --location $location `\n", 83 | " --kind OpenAI --sku s0" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": { 90 | "dotnet_interactive": { 91 | "language": "pwsh" 92 | }, 93 | "polyglot_notebook": { 94 | "kernelName": "pwsh" 95 | } 96 | }, 97 | "outputs": [], 98 | "source": [ 99 | "az cognitiveservices account show `\n", 100 | " --name $aiResourceName `\n", 101 | " --resource-group $resourceGroup | jq -r '.properties.endpoint'\n", 102 | "\n", 103 | "az cognitiveservices account keys list `\n", 104 | " --name $aiResourceName `\n", 105 | " --resource-group $resourceGroup | jq -r .key1" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": { 112 | "dotnet_interactive": { 113 | "language": "pwsh" 114 | }, 115 | "polyglot_notebook": { 116 | "kernelName": "pwsh" 117 | } 118 | }, 119 | "outputs": [], 120 | "source": [ 121 | "az cognitiveservices account `\n", 122 | " deployment create `\n", 123 | " --name $aiResourceName `\n", 124 | " --resource-group $resourceGroup `\n", 125 | " --deployment-name \"text-embedding-3-small\" `\n", 126 | " --model-name \"text-embedding-3-small\" `\n", 127 | " --model-version \"1\" `\n", 128 | " --model-format OpenAI `\n", 129 | " --sku-capacity \"1\" `\n", 130 | " --sku-name \"Standard\" `\n" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": { 137 | "dotnet_interactive": { 138 | "language": "pwsh" 139 | }, 140 | "polyglot_notebook": { 141 | "kernelName": "pwsh" 142 | } 143 | }, 144 | "outputs": [], 145 | "source": [ 146 | "az cognitiveservices account deployment list `\n", 147 | " --name $aiResourceName `\n", 148 | " --resource-group $resourceGroup" 149 | ] 150 | } 151 | ], 152 | "metadata": { 153 | "kernelspec": { 154 | "display_name": ".NET (C#)", 155 | "language": "C#", 156 | "name": ".net-csharp" 157 | }, 158 | "polyglot_notebook": { 159 | "kernelInfo": { 160 | "defaultKernelName": "csharp", 161 | "items": [ 162 | { 163 | "aliases": [], 164 | "languageName": "csharp", 165 | "name": "csharp" 166 | } 167 | ] 168 | } 169 | } 170 | }, 171 | "nbformat": 4, 172 | "nbformat_minor": 2 173 | } 174 | -------------------------------------------------------------------------------- /src/_infra/get-connection-string.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "dotnet_interactive": { 8 | "language": "csharp" 9 | }, 10 | "polyglot_notebook": { 11 | "kernelName": "csharp" 12 | } 13 | }, 14 | "outputs": [], 15 | "source": [ 16 | "#r \"nuget: dotenv.net\"" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": { 23 | "dotnet_interactive": { 24 | "language": "csharp" 25 | }, 26 | "polyglot_notebook": { 27 | "kernelName": "csharp" 28 | } 29 | }, 30 | "outputs": [], 31 | "source": [ 32 | "using dotenv.net;\n", 33 | "\n", 34 | "var envs = DotEnv.Read(new DotEnvOptions(envFilePaths: new[] {\"../../.env\"}));\n", 35 | "\n", 36 | "if (!envs.TryGetValue(\"PLAYGROUND_CONNECTION_STRING\", out var connectionStringInput)\n", 37 | " || string.IsNullOrEmpty(connectionStringInput))\n", 38 | "{\n", 39 | " connectionStringInput = await Microsoft.DotNet.Interactive.Kernel.GetInputAsync(\"Please provide a connection string.\");\n", 40 | "}\n", 41 | "\n", 42 | "var connectionString = new Uri(connectionStringInput);" 43 | ] 44 | } 45 | ], 46 | "metadata": { 47 | "kernelspec": { 48 | "display_name": ".NET (C#)", 49 | "language": "C#", 50 | "name": ".net-csharp" 51 | }, 52 | "polyglot_notebook": { 53 | "kernelInfo": { 54 | "defaultKernelName": "csharp", 55 | "items": [ 56 | { 57 | "aliases": [], 58 | "languageName": "csharp", 59 | "name": "csharp" 60 | } 61 | ] 62 | } 63 | } 64 | }, 65 | "nbformat": 4, 66 | "nbformat_minor": 2 67 | } 68 | -------------------------------------------------------------------------------- /src/_infra/setup-elastic-infrastructure.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Start the Elasticsearch container ![img](https://testcontainers.com/modules/elasticsearch//logo.svg)\n", 8 | "\n", 9 | "\n", 10 | "" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 2, 16 | "metadata": { 17 | "dotnet_interactive": { 18 | "language": "csharp" 19 | }, 20 | "polyglot_notebook": { 21 | "kernelName": "csharp" 22 | } 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "#r \"nuget: Testcontainers.Elasticsearch\"\n", 27 | "\n", 28 | "using Testcontainers.Elasticsearch;\n", 29 | "\n", 30 | "var elasticsearchContainer = new ElasticsearchBuilder()\n", 31 | " .WithPortBinding(9200, 9200)\n", 32 | " .WithPortBinding(9300, 9300)\n", 33 | " .WithReuse(true)\n", 34 | " .Build();\n", 35 | "await elasticsearchContainer.StartAsync();\n", 36 | "var connectionString = elasticsearchContainer.GetConnectionString();\n", 37 | "\n", 38 | "connectionString\n", 39 | "// https://elastic:elastic@127.0.0.1:9200/" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "### Shutdown (Optional)" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": { 53 | "dotnet_interactive": { 54 | "language": "csharp" 55 | }, 56 | "polyglot_notebook": { 57 | "kernelName": "csharp" 58 | } 59 | }, 60 | "outputs": [], 61 | "source": [ 62 | "await elasticsearchContainer.DisposeAsync();" 63 | ] 64 | } 65 | ], 66 | "metadata": { 67 | "kernelspec": { 68 | "display_name": ".NET (C#)", 69 | "language": "C#", 70 | "name": ".net-csharp" 71 | }, 72 | "polyglot_notebook": { 73 | "kernelInfo": { 74 | "defaultKernelName": "csharp", 75 | "items": [ 76 | { 77 | "aliases": [], 78 | "languageName": "csharp", 79 | "name": "csharp" 80 | } 81 | ] 82 | } 83 | } 84 | }, 85 | "nbformat": 4, 86 | "nbformat_minor": 2 87 | } 88 | -------------------------------------------------------------------------------- /src/elasticsearch-getting-started/00-quick-start.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Semantic Search Quick Start\n", 8 | "\n", 9 | "This interactive notebook will introduce you to some basic operations with **Elasticsearch**, using the official `Elastic.Clients.Elasticsearch` .NET client. You'll perform semantic search using Sentence Transformers for text embedding. Learn how to integrate traditional text-based search with semantic search, for a hybrid search system." 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "## Create Elastic Deployment\n", 17 | "\n", 18 | "I've notebook to run elastic using *Testcontainers*, navigate [src/_infra/setup-elastic-infrastructure.ipynb](../_infra/setup-elastic-infrastructure.ipynb) to run Elasticsearch container locally." 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "### Install packages and import modules" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": { 32 | "dotnet_interactive": { 33 | "language": "csharp" 34 | }, 35 | "polyglot_notebook": { 36 | "kernelName": "csharp" 37 | } 38 | }, 39 | "outputs": [], 40 | "source": [ 41 | "#r \"nuget: Elastic.Clients.Elasticsearch, 8.15.10\"\n", 42 | "#r \"nuget: System.Net.Http.Json, 8.0.1\"\n", 43 | "\n", 44 | "#!import ./Utils.cs\n", 45 | "#!import ../_infra/get-connection-string.ipynb" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "## Initialize the Elasticsearch client\n", 53 | "\n", 54 | "Now, we need to initialize the Elasticsearch client. We will use the [Elasticsearch client for .NET](https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/index.html) to connect to Elasticsearch." 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": { 61 | "dotnet_interactive": { 62 | "language": "csharp" 63 | }, 64 | "polyglot_notebook": { 65 | "kernelName": "csharp" 66 | } 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "using Elastic.Transport;\n", 71 | "using Elastic.Clients.Elasticsearch;\n", 72 | "using Elastic.Transport.Products.Elasticsearch;\n", 73 | "\n", 74 | "var elasticSettings = new ElasticsearchClientSettings(connectionString)\n", 75 | " .DisableDirectStreaming()\n", 76 | " .ServerCertificateValidationCallback(CertificateValidations.AllowAll);\n", 77 | "\n", 78 | "var client = new ElasticsearchClient(elasticSettings);" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "## Test the Client\n", 86 | "\n", 87 | "Before you continue, confirm that the client has connected with this test.\n" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": { 94 | "dotnet_interactive": { 95 | "language": "csharp" 96 | }, 97 | "polyglot_notebook": { 98 | "kernelName": "csharp" 99 | } 100 | }, 101 | "outputs": [], 102 | "source": [ 103 | "var info = await client.InfoAsync();\n", 104 | "\n", 105 | "DumpResponse(info);" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "## Setup the Embedding Model\n" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "metadata": { 119 | "dotnet_interactive": { 120 | "language": "csharp" 121 | }, 122 | "polyglot_notebook": { 123 | "kernelName": "csharp" 124 | } 125 | }, 126 | "outputs": [], 127 | "source": [ 128 | "#r \"nuget: Microsoft.Extensions.AI.OpenAI, 9.0.0-preview.*\"\n", 129 | "#r \"nuget: Azure.AI.OpenAI, 2.0.0\"" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": { 136 | "dotnet_interactive": { 137 | "language": "csharp" 138 | }, 139 | "polyglot_notebook": { 140 | "kernelName": "csharp" 141 | } 142 | }, 143 | "outputs": [], 144 | "source": [ 145 | "using Azure.AI.OpenAI;\n", 146 | "using Microsoft.Extensions.AI;\n", 147 | "\n", 148 | "AzureOpenAIClient aiClient = new AzureOpenAIClient(\n", 149 | " new Uri(envs[\"AZURE_OPENAI_ENDPOINT\"]),\n", 150 | " new System.ClientModel.ApiKeyCredential(envs[\"AZURE_OPENAI_APIKEY\"]));\n", 151 | "\n", 152 | "IEmbeddingGenerator> generator = aiClient\n", 153 | " .AsEmbeddingGenerator(modelId: \"text-embedding-3-small\");\n", 154 | "\n", 155 | "var textEmeddingDimension = 384;" 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "## Index some test data\n", 163 | "Our client is set up and connected to our Elastic deployment. Now we need some data to test out the basics of Elasticsearch queries. We'll use a small index of books with the following fields:" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "metadata": { 170 | "dotnet_interactive": { 171 | "language": "csharp" 172 | }, 173 | "polyglot_notebook": { 174 | "kernelName": "csharp" 175 | } 176 | }, 177 | "outputs": [], 178 | "source": [ 179 | "using System.Text.Json.Serialization;\n", 180 | "\n", 181 | "public class Book\n", 182 | "{\n", 183 | " [JsonPropertyName(\"title\")]\n", 184 | " public string Title { get; set; }\n", 185 | "\n", 186 | " [JsonPropertyName(\"summary\")]\n", 187 | " public string Summary { get; set; }\n", 188 | "\n", 189 | " [JsonPropertyName(\"authors\")]\n", 190 | " public List Authors { get; set; }\n", 191 | "\n", 192 | " [JsonPropertyName(\"publish_date\")]\n", 193 | " public DateTime publish_date { get; set; }\n", 194 | "\n", 195 | " [JsonPropertyName(\"num_reviews\")]\n", 196 | " public int num_reviews { get; set; }\n", 197 | "\n", 198 | " [JsonPropertyName(\"publisher\")]\n", 199 | " public string Publisher { get; set; }\n", 200 | "\n", 201 | "\n", 202 | " public float[] TitleVector { get; set; }\n", 203 | "}" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "## Create an index\n", 211 | "\n", 212 | "First ensure that you do not have a previously created index with the name `book_index`." 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": null, 218 | "metadata": { 219 | "dotnet_interactive": { 220 | "language": "csharp" 221 | }, 222 | "polyglot_notebook": { 223 | "kernelName": "csharp" 224 | } 225 | }, 226 | "outputs": [], 227 | "source": [ 228 | "var deleteIndexResponse = await client.Indices.DeleteAsync(\"book_index\");\n", 229 | "\n", 230 | "Dump(deleteIndexResponse);" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "Let's create an Elasticsearch index with the correct mappings for our test data." 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "metadata": { 244 | "dotnet_interactive": { 245 | "language": "csharp" 246 | }, 247 | "polyglot_notebook": { 248 | "kernelName": "csharp" 249 | } 250 | }, 251 | "outputs": [], 252 | "source": [ 253 | "using Elastic.Clients.Elasticsearch;\n", 254 | "using Elastic.Clients.Elasticsearch.IndexManagement;\n", 255 | "using Elastic.Clients.Elasticsearch.Mapping;\n", 256 | "\n", 257 | "var indexResponse = await client.Indices.CreateAsync(\"book_index\", d =>\n", 258 | " d.Mappings(m => m\n", 259 | " .Properties(pp => pp\n", 260 | " .Text(p => p.Title)\n", 261 | " .DenseVector(Infer.Property(p => p.TitleVector),\n", 262 | " d => d\n", 263 | " .Dims(textEmeddingDimension)\n", 264 | " .Index(true)\n", 265 | " .Similarity(DenseVectorSimilarity.Cosine))\n", 266 | " .Text(p => p.Summary)\n", 267 | " .Date(p => p.publish_date)\n", 268 | " .IntegerNumber(p => p.num_reviews)\n", 269 | " .Keyword(p => p.Publisher)\n", 270 | " .Keyword(p => p.Authors)\n", 271 | " )\n", 272 | " ));\n", 273 | "\n", 274 | "DumpRequest(indexResponse);" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": {}, 280 | "source": [ 281 | "## Index test data\n", 282 | "\n", 283 | "Run the following command to upload some test data, containing information about 10 popular programming books from this dataset." 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "metadata": { 290 | "dotnet_interactive": { 291 | "language": "csharp" 292 | }, 293 | "polyglot_notebook": { 294 | "kernelName": "csharp" 295 | } 296 | }, 297 | "outputs": [], 298 | "source": [ 299 | "using System.Net.Http;\n", 300 | "using System.Net.Http.Json;\n", 301 | "\n", 302 | "var http = new HttpClient();\n", 303 | "var url = \"https://raw.githubusercontent.com/elastic/elasticsearch-labs/main/notebooks/search/data.json\";\n", 304 | "var books = await http.GetFromJsonAsync(url);\n", 305 | "\n", 306 | "// books.DisplayTable();" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": null, 312 | "metadata": { 313 | "dotnet_interactive": { 314 | "language": "csharp" 315 | }, 316 | "polyglot_notebook": { 317 | "kernelName": "csharp" 318 | } 319 | }, 320 | "outputs": [], 321 | "source": [ 322 | "async Task ToEmbedding(string text) {\n", 323 | " GeneratedEmbeddings> embeddings = await generator\n", 324 | " .GenerateAsync([text], new EmbeddingGenerationOptions{\n", 325 | " Dimensions = textEmeddingDimension\n", 326 | " });\n", 327 | "\n", 328 | " return embeddings.First().Vector.ToArray();\n", 329 | "}\n", 330 | "\n", 331 | "async Task> ToEmbeddings(IEnumerable items) {\n", 332 | " GeneratedEmbeddings> embeddings = await generator\n", 333 | " .GenerateAsync(items, new EmbeddingGenerationOptions{\n", 334 | " Dimensions = textEmeddingDimension\n", 335 | " });\n", 336 | "\n", 337 | " return embeddings.Select(x => x.Vector.ToArray());\n", 338 | "}\n", 339 | "\n", 340 | "var embedding = await ToEmbedding(\"The quick brown fox jumps over the lazy dog\");\n", 341 | "display($\"Dimensions length = {embedding.Length}\");\n" 342 | ] 343 | }, 344 | { 345 | "cell_type": "markdown", 346 | "metadata": {}, 347 | "source": [ 348 | "`ToEmbedding` will encode the text into a vector on the fly, using the model we initialized earlier." 349 | ] 350 | }, 351 | { 352 | "cell_type": "code", 353 | "execution_count": null, 354 | "metadata": { 355 | "dotnet_interactive": { 356 | "language": "csharp" 357 | }, 358 | "polyglot_notebook": { 359 | "kernelName": "csharp" 360 | } 361 | }, 362 | "outputs": [], 363 | "source": [ 364 | "var embeddings = (await ToEmbedding(books.Select(x => x.Title))).ToArray();\n", 365 | "for(var i = 0; i < embeddings.Length; i++)\n", 366 | "{\n", 367 | " books[i].TitleVector = embeddings[i];\n", 368 | "}" 369 | ] 370 | }, 371 | { 372 | "cell_type": "markdown", 373 | "metadata": {}, 374 | "source": [ 375 | "Now we can use Bulk API to upload data to Elasticsearch." 376 | ] 377 | }, 378 | { 379 | "cell_type": "code", 380 | "execution_count": null, 381 | "metadata": { 382 | "dotnet_interactive": { 383 | "language": "csharp" 384 | }, 385 | "polyglot_notebook": { 386 | "kernelName": "csharp" 387 | } 388 | }, 389 | "outputs": [], 390 | "source": [ 391 | "var bulkResponse = await client.BulkAsync(\"book_index\", d => d\n", 392 | " .IndexMany(books, (bd, b) => bd.Index(\"book_index\"))\n", 393 | ");\n", 394 | "\n", 395 | "bulkResponse.Display();" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": [ 402 | "## Making queries\n", 403 | "\n", 404 | "Let's use the keyword search to see if we have relevant data indexed." 405 | ] 406 | }, 407 | { 408 | "cell_type": "code", 409 | "execution_count": null, 410 | "metadata": { 411 | "dotnet_interactive": { 412 | "language": "csharp" 413 | }, 414 | "polyglot_notebook": { 415 | "kernelName": "csharp" 416 | } 417 | }, 418 | "outputs": [], 419 | "source": [ 420 | "var searchResponse = await client.SearchAsync(s => s\n", 421 | " .Index(\"book_index\")\n", 422 | " .Query(q => q.Match(m => m.Field(f => f.Title).Query(\"JavaScript\")))\n", 423 | ");\n", 424 | "\n", 425 | "DumpRequest(searchResponse);\n", 426 | "searchResponse.Documents.Select(x => x.Title).DisplayTable();" 427 | ] 428 | }, 429 | { 430 | "cell_type": "markdown", 431 | "metadata": {}, 432 | "source": [ 433 | "Now that we have indexed the books, we want to perform a semantic search for books that are similar to a given query. We embed the query and perform a search. " 434 | ] 435 | }, 436 | { 437 | "cell_type": "code", 438 | "execution_count": null, 439 | "metadata": { 440 | "dotnet_interactive": { 441 | "language": "csharp" 442 | }, 443 | "polyglot_notebook": { 444 | "kernelName": "csharp" 445 | } 446 | }, 447 | "outputs": [], 448 | "source": [ 449 | "var searchQuery = \"javascript books\";\n", 450 | "var queryEmbedding = await ToEmbedding(searchQuery);\n", 451 | "var searchResponse = await client.SearchAsync(s => s\n", 452 | " .Index(\"book_index\")\n", 453 | " .Knn(d => d\n", 454 | " .Field(f => f.TitleVector)\n", 455 | " .QueryVector(queryEmbedding)\n", 456 | " .k(5)\n", 457 | " .NumCandidates(100))\n", 458 | ");\n", 459 | "\n", 460 | "var threshold = 0.7;\n", 461 | "searchResponse.Hits\n", 462 | " .Where(x => x.Score > threshold)\n", 463 | " .Select(x => new { x.Source.Title, x.Score })\n", 464 | " .DisplayTable();" 465 | ] 466 | }, 467 | { 468 | "cell_type": "markdown", 469 | "metadata": {}, 470 | "source": [ 471 | "## Filtering\n", 472 | "\n", 473 | "Filter context is mostly used for filtering structured data. For example, use filter context to answer questions like:\n", 474 | "\n", 475 | "* Does this timestamp fall into the range 2015 to 2016?\n", 476 | "* Is the status field set to \"published\"?\n", 477 | "\n", 478 | "Filter context is in effect whenever a query clause is passed to a filter parameter, such as the filter or must_not parameters in a bool query.\n", 479 | "\n", 480 | "[Learn more](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html#filter-context) about filter context in the Elasticsearch docs." 481 | ] 482 | }, 483 | { 484 | "cell_type": "markdown", 485 | "metadata": {}, 486 | "source": [ 487 | "### Example: Keyword Filtering\n", 488 | "This is an example of adding a keyword filter to the query.\n", 489 | "\n", 490 | "The example retrieves the top books that are similar to \"javascript books\" based on their title vectors, and also Addison-Wesley as publisher." 491 | ] 492 | }, 493 | { 494 | "cell_type": "code", 495 | "execution_count": null, 496 | "metadata": { 497 | "dotnet_interactive": { 498 | "language": "csharp" 499 | }, 500 | "polyglot_notebook": { 501 | "kernelName": "csharp" 502 | } 503 | }, 504 | "outputs": [], 505 | "source": [ 506 | "var searchQuery = \"javascript books\";\n", 507 | "var queryEmbedding = await ToEmbedding(searchQuery);\n", 508 | "var searchResponse = await client.SearchAsync(s => s\n", 509 | " .Index(\"book_index\")\n", 510 | " .Knn(d => d\n", 511 | " .Field(f => f.TitleVector)\n", 512 | " .QueryVector(queryEmbedding)\n", 513 | " .k(5)\n", 514 | " .NumCandidates(100)\n", 515 | " .Filter(f => f.Term(t => t.Field(p => p.Publisher).Value(\"addison-wesley\"))) \n", 516 | " )\n", 517 | ");\n", 518 | "\n", 519 | "searchResponse.Hits\n", 520 | " .Select(x => new { x.Source.Title, x.Score })\n", 521 | " .DisplayTable(); " 522 | ] 523 | } 524 | ], 525 | "metadata": { 526 | "kernelspec": { 527 | "display_name": ".NET (C#)", 528 | "language": "C#", 529 | "name": ".net-csharp" 530 | }, 531 | "polyglot_notebook": { 532 | "kernelInfo": { 533 | "defaultKernelName": "csharp", 534 | "items": [ 535 | { 536 | "aliases": [], 537 | "languageName": "csharp", 538 | "name": "csharp" 539 | } 540 | ] 541 | } 542 | } 543 | }, 544 | "nbformat": 4, 545 | "nbformat_minor": 2 546 | } 547 | -------------------------------------------------------------------------------- /src/elasticsearch-getting-started/02-hybrid-search.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Hybrid Search using RRF\n", 8 | "\n", 9 | "In this example we'll use the *reciprocal rank fusion algorithm* to combine the results of **BM25** and **kNN semantic search**. We'll use the same dataset we used in our quickstart guide.\n", 10 | "\n", 11 | "You can use RRF for hybrid search out of the box, without any additional configuration. This example demonstrates how RRF ranking works at a basic level." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## Initialize the Elasticsearch client\n", 19 | "\n", 20 | "Now, we need to initialize the Elasticsearch client. We will use the [Elasticsearch client for .NET](https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/index.html) to connect to Elasticsearch.\n" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "#### Install packages and import modules" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "#r \"nuget: Elastic.Clients.Elasticsearch\"\n", 37 | "\n", 38 | "#!import ./Utils.cs\n", 39 | "\n", 40 | "#!set --name elasticCloudId --value @input\n", 41 | "#!set --name elasticCloudKey --value @input" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "#### Initialize Client" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "using Elastic.Transport;\n", 58 | "using Elastic.Clients.Elasticsearch;\n", 59 | "using Elastic.Transport.Products.Elasticsearch;\n", 60 | "\n", 61 | "var elasticSettings = new ElasticsearchClientSettings(\n", 62 | " elasticCloudId, new ApiKey(elasticCloudKey))\n", 63 | " .DisableDirectStreaming()\n", 64 | " .ServerCertificateValidationCallback(CertificateValidations.AllowAll);\n", 65 | "\n", 66 | "var client = new ElasticsearchClient(elasticSettings);" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "#### Test the Client\n", 74 | "\n", 75 | "Before you continue, confirm that the client has connected with this test.\n" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "var info = await client.InfoAsync();\n", 85 | "\n", 86 | "DumpResponse(info);" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "## Define Model\n" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "using System.Text.Json.Serialization;\n", 103 | "\n", 104 | "public class Book\n", 105 | "{\n", 106 | " [JsonPropertyName(\"title\")]\n", 107 | " public string Title { get; set; }\n", 108 | "\n", 109 | " [JsonPropertyName(\"summary\")]\n", 110 | " public string Summary { get; set; }\n", 111 | "\n", 112 | " [JsonPropertyName(\"authors\")]\n", 113 | " public List Authors { get; set; }\n", 114 | "\n", 115 | " [JsonPropertyName(\"publish_date\")]\n", 116 | " public DateTime publish_date { get; set; }\n", 117 | "\n", 118 | " [JsonPropertyName(\"num_reviews\")]\n", 119 | " public int num_reviews { get; set; }\n", 120 | "\n", 121 | " [JsonPropertyName(\"publisher\")]\n", 122 | " public string Publisher { get; set; }\n", 123 | "\n", 124 | "\n", 125 | " public float[] TitleVector { get; set; }\n", 126 | "}" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "#### Pretty printing Elasticsearch responses\n", 134 | "\n", 135 | "Let's add a helper function to print Elasticsearch responses in a readable format." 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [ 144 | "void PrettyPrint(SearchResponse searchResponse) => searchResponse.Hits\n", 145 | " .Select(x => new { \n", 146 | " Title = x.Source.Title,\n", 147 | " Score = x.Score,\n", 148 | " Rank = x.Rank,\n", 149 | " Summary = x.Source.Summary,\n", 150 | " })\n", 151 | " .DisplayTable();" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": [ 158 | "### Setup the Embedding Model\n" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "#r \"nuget: Microsoft.Extensions.AI.OpenAI, 9.0.0-preview.*\"\n", 168 | "#r \"nuget: Azure.AI.OpenAI, 2.0.0\"" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": null, 174 | "metadata": {}, 175 | "outputs": [], 176 | "source": [ 177 | "using Azure.AI.OpenAI;\n", 178 | "using Microsoft.Extensions.AI;\n", 179 | "\n", 180 | "AzureOpenAIClient aiClient = new AzureOpenAIClient(\n", 181 | " new Uri(envs[\"AZURE_OPENAI_ENDPOINT\"]),\n", 182 | " new System.ClientModel.ApiKeyCredential(envs[\"AZURE_OPENAI_APIKEY\"]));\n", 183 | "\n", 184 | "IEmbeddingGenerator> generator = aiClient\n", 185 | " .AsEmbeddingGenerator(modelId: \"text-embedding-3-small\");\n", 186 | "\n", 187 | "var textEmeddingDimension = 384;\n", 188 | "\n", 189 | "async Task ToEmbedding(string text) {\n", 190 | " GeneratedEmbeddings> embeddings = await generator\n", 191 | " .GenerateAsync([text], new EmbeddingGenerationOptions{\n", 192 | " Dimensions = textEmeddingDimension\n", 193 | " });\n", 194 | "\n", 195 | " return embeddings.First().Vector.ToArray();\n", 196 | "}" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": {}, 202 | "source": [ 203 | "## Querying Documents with Hybrid Search\n", 204 | "\n", 205 | "🔐 NOTE: to run the queries that follow you need the book_index dataset from our [00-quick-start.ipynb](./00-quick-start.ipynb). If you haven't worked through the quick start, please follow the steps described there to create an Elasticsearch deployment with the dataset in it, and then come back to run the queries here.\n", 206 | "\n", 207 | "Now we need to perform a query using two different search strategies:\n", 208 | "\n", 209 | "* Semantic search using the \"all-MiniLM-L6-v2\" embedding model\n", 210 | "* Keyword search using the \"title\" field\n", 211 | "\n", 212 | "We then use *Reciprocal Rank Fusion (RRF)* to balance the scores to provide a final list of documents, ranked in order of relevance. RRF is a ranking algorithm for combining results from different information retrieval strategies.\n", 213 | "\n", 214 | "Note tha we use `_rank` to show our top-ranked documents." 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": null, 220 | "metadata": {}, 221 | "outputs": [ 222 | { 223 | "data": { 224 | "text/csv": [ 225 | "Title,Score,Rank,Summary\r\n", 226 | "\"Python Crash Course\",0.032786883,1,\"A fast-paced, no-nonsense guide to programming in Python\"\r\n", 227 | "\"The Pragmatic Programmer: Your Journey to Mastery\",0.03175403,2,\"A guide to pragmatic programming for software engineers and developers\"\r\n", 228 | "\"Eloquent JavaScript\",0.016129032,3,\"A modern introduction to programming\"\r\n", 229 | "\"You Don't Know JS: Up & Going\",0.015873017,4,\"Introduction to JavaScript and programming as a whole\"\r\n", 230 | "\"JavaScript: The Good Parts\",0.015873017,5,\"A deep dive into the parts of JavaScript that are essential to writing maintainable code\"\r\n", 231 | "\"The Clean Coder: A Code of Conduct for Professional Programmers\",0.015625,6,\"A guide to professional conduct in the field of software engineering\"\r\n", 232 | "\"Design Patterns: Elements of Reusable Object-Oriented Software\",0.015384615,7,\"Guide to design patterns that can be used in any object-oriented language\"\r\n" 233 | ], 234 | "text/html": [ 235 | "
TitleScoreRankSummary
Python Crash Course
0.032786883
1
A fast-paced, no-nonsense guide to programming in Python
The Pragmatic Programmer: Your Journey to Mastery
0.03175403
2
A guide to pragmatic programming for software engineers and developers
Eloquent JavaScript
0.016129032
3
A modern introduction to programming
You Don't Know JS: Up & Going
0.015873017
4
Introduction to JavaScript and programming as a whole
JavaScript: The Good Parts
0.015873017
5
A deep dive into the parts of JavaScript that are essential to writing maintainable code
The Clean Coder: A Code of Conduct for Professional Programmers
0.015625
6
A guide to professional conduct in the field of software engineering
Design Patterns: Elements of Reusable Object-Oriented Software
0.015384615
7
Guide to design patterns that can be used in any object-oriented language
" 267 | ] 268 | }, 269 | "metadata": {}, 270 | "output_type": "display_data" 271 | } 272 | ], 273 | "source": [ 274 | "var searchQuery = \"python programming\";\n", 275 | "var queryEmbedding = await ToEmbedding(searchQuery);\n", 276 | "\n", 277 | "var searchResponse = await client.SearchAsync(s => s\n", 278 | " .Index(\"book_index\")\n", 279 | " .Query(d => d.Match(m => m.Field(f => f.Summary).Query(searchQuery)))\n", 280 | " .Knn(d => d\n", 281 | " .Field(f => f.TitleVector)\n", 282 | " .QueryVector(queryEmbedding)\n", 283 | " .k(5)\n", 284 | " .NumCandidates(10))\n", 285 | " .Rank(r => r.Rrf(rrf => {}))\n", 286 | ");\n", 287 | "\n", 288 | "// DumpRequest(searchResponse);\n", 289 | "PrettyPrint(searchResponse);\n" 290 | ] 291 | } 292 | ], 293 | "metadata": { 294 | "kernelspec": { 295 | "display_name": ".NET (C#)", 296 | "language": "C#", 297 | "name": ".net-csharp" 298 | }, 299 | "polyglot_notebook": { 300 | "kernelInfo": { 301 | "defaultKernelName": "csharp", 302 | "items": [ 303 | { 304 | "aliases": [], 305 | "languageName": "csharp", 306 | "name": "csharp" 307 | } 308 | ] 309 | } 310 | } 311 | }, 312 | "nbformat": 4, 313 | "nbformat_minor": 2 314 | } 315 | -------------------------------------------------------------------------------- /src/elasticsearch-getting-started/README.md: -------------------------------------------------------------------------------- 1 | # Search notebooks 2 | 3 | This folder contains a number of notebooks that demonstrate the fundamentals of Elasticsearch, like indexing embeddings, running lexical, semantic and _hybrid_ searches, and more. 4 | 5 | The following notebooks are available: 6 | 7 | 0. [Quick start](#0-quick-start) 8 | 1. [Keyword, querying, filtering](#1-keyword-querying-filtering) 9 | 10 | 11 | ## Notebooks 12 | 13 | ### 0. Quick start 14 | 15 | In the [`00-quick-start.ipynb`](./00-quick-start.ipynb) notebook you'll learn how to: 16 | 17 | - Use the Elasticsearch Python client for various operations. 18 | - Create and define an index for a sample dataset with `dense_vector` fields. 19 | - Transform book titles into embeddings using [Sentence Transformers](https://www.sbert.net) and index them into Elasticsearch. 20 | - Perform k-nearest neighbors (knn) semantic searches. 21 | 22 | ### 1. Keyword, querying, filtering 23 | 24 | In the [`01-keyword-querying-filtering.ipynb`](./01-keyword-querying-filtering.ipynb) notebook, you'll learn how to: 25 | 26 | - Use [query and filter contexts](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html) to search and filter documents in Elasticsearch. 27 | - Execute full-text searches with `match` and `multi-match` queries. 28 | - Query and filter documents based on `text`, `number`, `date`, or `boolean` values. 29 | - Run multi-field searches using the `multi-match` query. 30 | - Prioritize specific fields in the `multi-match` query for tailored results. -------------------------------------------------------------------------------- /src/elasticsearch-getting-started/Utils.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.IO; 3 | using Elastic.Clients.Elasticsearch; 4 | using Elastic.Transport.Products.Elasticsearch; 5 | using System.Text.Json.Nodes; 6 | 7 | 8 | static void Dump(ElasticsearchResponse response) 9 | { 10 | response.DebugInformation.DisplayAs("application/json"); 11 | } 12 | 13 | static void DumpResponse(ElasticsearchResponse response) 14 | { 15 | ToJson(Response(response)).DisplayAs("application/json"); 16 | } 17 | 18 | static object Response(ElasticsearchResponse response) 19 | { 20 | return TryParsePayload(GetResponseFromDebugInformation(response.DebugInformation)); 21 | } 22 | 23 | static void DumpRequest(ElasticsearchResponse response) 24 | { 25 | ToJson(Request(response)).DisplayAs("application/json"); 26 | } 27 | 28 | static object Request(ElasticsearchResponse response) 29 | { 30 | return TryParsePayload(GetRequestFromDebugInformation(response.DebugInformation)); 31 | } 32 | 33 | static object TryParsePayload(string payload) 34 | { 35 | try 36 | { 37 | return JsonDocument.Parse(payload.Trim()); 38 | } 39 | catch 40 | { 41 | return payload; 42 | } 43 | } 44 | 45 | static string ToJson(object paylod) 46 | { 47 | return Indent(prettyJson(System.Text.Json.JsonSerializer.Serialize(paylod))); 48 | 49 | static string prettyJson(string json) 50 | { 51 | try 52 | { 53 | var jsonDocument = JsonDocument.Parse(json); 54 | var options = new JsonSerializerOptions { WriteIndented = true }; 55 | string prettyJson = JsonSerializer.Serialize(jsonDocument, options); 56 | 57 | return prettyJson; 58 | } 59 | catch (Exception) 60 | { 61 | return json; 62 | } 63 | } 64 | } 65 | 66 | static string Indent(string json) 67 | { 68 | json = JsonNode.Parse(json).ToJsonString(new JsonSerializerOptions 69 | { 70 | WriteIndented = true, 71 | // Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping 72 | }); 73 | return json; 74 | } 75 | 76 | static string GetRequestFromDebugInformation(string paylod) 77 | { 78 | var request = paylod 79 | .Split(new[] { "# Request:" }, StringSplitOptions.None)[1] 80 | .Split(new[] { "# Response:" }, StringSplitOptions.None)[0]; 81 | 82 | return request; 83 | } 84 | 85 | static string GetResponseFromDebugInformation(string paylod) 86 | { 87 | var response = paylod.Split(new[] { "# Response:" }, StringSplitOptions.None)[1]; 88 | 89 | return response; 90 | } 91 | -------------------------------------------------------------------------------- /src/elasticsearch-getting-started/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "The Pragmatic Programmer: Your Journey to Mastery", 4 | "authors": ["andrew hunt", "david thomas"], 5 | "summary": "A guide to pragmatic programming for software engineers and developers", 6 | "publish_date": "2019-10-29", 7 | "num_reviews": 30, 8 | "publisher": "addison-wesley" 9 | }, 10 | { 11 | "title": "Python Crash Course", 12 | "authors": ["eric matthes"], 13 | "summary": "A fast-paced, no-nonsense guide to programming in Python", 14 | "publish_date": "2019-05-03", 15 | "num_reviews": 42, 16 | "publisher": "no starch press" 17 | }, 18 | { 19 | "title": "Artificial Intelligence: A Modern Approach", 20 | "authors": ["stuart russell", "peter norvig"], 21 | "summary": "Comprehensive introduction to the theory and practice of artificial intelligence", 22 | "publish_date": "2020-04-06", 23 | "num_reviews": 39, 24 | "publisher": "pearson" 25 | }, 26 | { 27 | "title": "Clean Code: A Handbook of Agile Software Craftsmanship", 28 | "authors": ["robert c. martin"], 29 | "summary": "A guide to writing code that is easy to read, understand and maintain", 30 | "publish_date": "2008-08-11", 31 | "num_reviews": 55, 32 | "publisher": "prentice hall" 33 | }, 34 | { 35 | "title": "You Don't Know JS: Up & Going", 36 | "authors": ["kyle simpson"], 37 | "summary": "Introduction to JavaScript and programming as a whole", 38 | "publish_date": "2015-03-27", 39 | "num_reviews": 36, 40 | "publisher": "oreilly" 41 | }, 42 | { 43 | "title": "Eloquent JavaScript", 44 | "authors": ["marijn haverbeke"], 45 | "summary": "A modern introduction to programming", 46 | "publish_date": "2018-12-04", 47 | "num_reviews": 38, 48 | "publisher": "no starch press" 49 | }, 50 | { 51 | "title": "Design Patterns: Elements of Reusable Object-Oriented Software", 52 | "authors": [ 53 | "erich gamma", 54 | "richard helm", 55 | "ralph johnson", 56 | "john vlissides" 57 | ], 58 | "summary": "Guide to design patterns that can be used in any object-oriented language", 59 | "publish_date": "1994-10-31", 60 | "num_reviews": 45, 61 | "publisher": "addison-wesley" 62 | }, 63 | { 64 | "title": "The Clean Coder: A Code of Conduct for Professional Programmers", 65 | "authors": ["robert c. martin"], 66 | "summary": "A guide to professional conduct in the field of software engineering", 67 | "publish_date": "2011-05-13", 68 | "num_reviews": 20, 69 | "publisher": "prentice hall" 70 | }, 71 | { 72 | "title": "JavaScript: The Good Parts", 73 | "authors": ["douglas crockford"], 74 | "summary": "A deep dive into the parts of JavaScript that are essential to writing maintainable code", 75 | "publish_date": "2008-05-15", 76 | "num_reviews": 51, 77 | "publisher": "oreilly" 78 | }, 79 | { 80 | "title": "Introduction to the Theory of Computation", 81 | "authors": ["michael sipser"], 82 | "summary": "Introduction to the theory of computation and complexity theory", 83 | "publish_date": "2012-06-27", 84 | "num_reviews": 33, 85 | "publisher": "cengage learning" 86 | } 87 | ] 88 | -------------------------------------------------------------------------------- /src/nest-vs-elasticsearch/Utils.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.IO; 3 | using Nest; 4 | using Elastic.Clients.Elasticsearch; 5 | using Elastic.Transport.Products.Elasticsearch; 6 | 7 | static void ReportAndAssert( 8 | string testCaseName, 9 | IResponse nestResponse, 10 | ElasticsearchResponse elasticResponse) 11 | { 12 | ReportAndAssert(testCaseName, nestResponse.DebugInformation, elasticResponse.DebugInformation); 13 | } 14 | 15 | static void ReportAndAssert(string testCaseName, string nestResponse, string elasticResponse) 16 | { 17 | display($"Test case: '{testCaseName}' 🚀"); 18 | display(nestResponse); 19 | display(elasticResponse); 20 | 21 | var file1 = $"../report/{testCaseName}.nest.txt"; 22 | var file2 = $"../report/{testCaseName}.net.txt"; 23 | var (payload1, payload2) = (GetRequestResponseString(nestResponse), GetRequestResponseString(elasticResponse)); 24 | 25 | System.IO.File.WriteAllText(file1, payload1); 26 | System.IO.File.WriteAllText(file2, payload2); 27 | 28 | try 29 | { 30 | Xunit.Assert.Equal(payload1.Substring(0, payload1.IndexOf("Response:")), payload2.Substring(0, payload2.IndexOf("Response:"))); 31 | 32 | display($"'{testCaseName}' passed ✅"); 33 | } 34 | catch (Xunit.Sdk.XunitException ex) 35 | { 36 | display(ex.Message); 37 | display($"'{testCaseName}' failed ❌"); 38 | } 39 | } 40 | 41 | static void DumpResponse(IResponse response) 42 | { 43 | DumpResponseCore(response.DebugInformation); 44 | } 45 | static void DumpResponse(ElasticsearchResponse response) 46 | { 47 | DumpResponseCore(response.DebugInformation); 48 | } 49 | 50 | static void DumpResponseCore(string debugInformation) 51 | { 52 | display(GetGeneralInfoFromDebugInformation(debugInformation)); 53 | display("⚙️❔Request:"); 54 | display(TryParsePayload(GetRequestFromDebugInformation(debugInformation))); 55 | display("⚙️🟰Response:"); 56 | display(TryParsePayload(GetResponseFromDebugInformation(debugInformation))); 57 | } 58 | 59 | static object TryParsePayload(string payload) 60 | { 61 | try 62 | { 63 | return JsonDocument.Parse(payload); 64 | } 65 | catch 66 | { 67 | return string.Empty; 68 | } 69 | } 70 | 71 | static string GetRequestResponseString(string paylod) 72 | { 73 | var request = GetRequestFromDebugInformation(paylod); 74 | 75 | var response = GetResponseFromDebugInformation(paylod); 76 | 77 | var resultPayload = $""" 78 | Request: 79 | {prettyJson(request)} 80 | Response: 81 | {prettyJson(response)} 82 | """; 83 | 84 | return resultPayload; 85 | 86 | static string prettyJson(string json) 87 | { 88 | try 89 | { 90 | var jsonDocument = JsonDocument.Parse(json); 91 | var options = new JsonSerializerOptions { WriteIndented = true }; 92 | string prettyJson = JsonSerializer.Serialize(jsonDocument, options); 93 | 94 | return prettyJson; 95 | } 96 | catch (Exception) 97 | { 98 | return json; 99 | } 100 | } 101 | } 102 | 103 | static string GetGeneralInfoFromDebugInformation(string paylod) 104 | { 105 | var response = paylod.Split(new[] { "# Request:" }, StringSplitOptions.None)[0]; 106 | 107 | return response; 108 | } 109 | 110 | static string GetRequestFromDebugInformation(string paylod) 111 | { 112 | var request = paylod 113 | .Split(new[] { "# Request:" }, StringSplitOptions.None)[1] 114 | .Split(new[] { "# Response:" }, StringSplitOptions.None)[0]; 115 | 116 | return request; 117 | } 118 | 119 | static string GetResponseFromDebugInformation(string paylod) 120 | { 121 | var response = paylod.Split(new[] { "# Response:" }, StringSplitOptions.None)[1]; 122 | 123 | return response; 124 | } 125 | -------------------------------------------------------------------------------- /src/nest-vs-elasticsearch/common.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "dotnet_interactive": { 8 | "language": "csharp" 9 | }, 10 | "polyglot_notebook": { 11 | "kernelName": "csharp" 12 | } 13 | }, 14 | "outputs": [], 15 | "source": [ 16 | "#r \"nuget: NEST\"\n", 17 | "#r \"nuget: Elastic.Clients.Elasticsearch\"\n", 18 | "#r \"nuget: Testcontainers.Elasticsearch\"\n", 19 | "#r \"nuget: XUnit\"\n", 20 | "#r \"nuget: Bogus\"\n", 21 | "#r \"nuget: Microsoft.Data.Analysis\"\n", 22 | "#r \"nuget: dotenv.net\"\n", 23 | "#r \"nuget: System.Linq.Async\"" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 2, 29 | "metadata": { 30 | "dotnet_interactive": { 31 | "language": "csharp" 32 | }, 33 | "polyglot_notebook": { 34 | "kernelName": "csharp" 35 | } 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "#!import ./Utils.cs\n", 40 | "\n", 41 | "public class BlogPost\n", 42 | "{\n", 43 | " public int Id { get; set; }\n", 44 | " public string Title { get; set; }\n", 45 | " public string Description { get; set; }\n", 46 | " public User Author { get; set; }\n", 47 | " public DateTime CreatedAt { get; set; }\n", 48 | " public bool IsPublished { get; set; }\n", 49 | " public Like[] Likes { get; set; }\n", 50 | "}\n", 51 | "\n", 52 | "public class Like\n", 53 | "{\n", 54 | " public int Id { get; set; }\n", 55 | " public int PostId { get; set; }\n", 56 | " public string AuthorId { get; set; }\n", 57 | " public DateTime CreatedAt { get; set; }\n", 58 | "}\n", 59 | "\n", 60 | "public class User\n", 61 | "{\n", 62 | " public int Id { get; set; }\n", 63 | " public string Name { get; set; }\n", 64 | " public string Email { get; set; }\n", 65 | "}" 66 | ] 67 | } 68 | ], 69 | "metadata": { 70 | "kernelspec": { 71 | "display_name": ".NET (C#)", 72 | "language": "C#", 73 | "name": ".net-csharp" 74 | }, 75 | "polyglot_notebook": { 76 | "kernelInfo": { 77 | "defaultKernelName": "csharp", 78 | "items": [ 79 | { 80 | "aliases": [], 81 | "languageName": "csharp", 82 | "name": "csharp" 83 | } 84 | ] 85 | } 86 | } 87 | }, 88 | "nbformat": 4, 89 | "nbformat_minor": 2 90 | } 91 | -------------------------------------------------------------------------------- /src/nest-vs-elasticsearch/playground.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Playground for Elasticsearch.NET and NEST clients\n", 8 | "\n", 9 | "This playground is a collection of notebooks that demonstrate how to use Elasticsearch.NET and NEST clients.\n", 10 | "\n", 11 | "## Prerequisites\n", 12 | "\n", 13 | "* Get a connection string to your Elasticsearch instance, you may want to use the local installation, see [setup-elastic-infrastructure.ipynb](./setup-elastic-infrastructure.ipynb)\n", 14 | "\n", 15 | "## Scope\n", 16 | "\n", 17 | "- Index Management. Mappings\n", 18 | "- Searching\n", 19 | "- ✅ Scrolling\n", 20 | "- Bulk Operations\n", 21 | "- Aggregations\n", 22 | "- Scripting" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "## Index Management\n", 30 | "\n", 31 | "See the [setup-nest.ipynb](./setup-nest.ipynb) notebook for details.\n", 32 | "\n", 33 | "### Breaking changes:\n", 34 | "\n", 35 | "* `Elastic.Clients.Elasticsearch:8.15.6` does not support `AutoMap`." 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "## Searching for data\n", 43 | "\n", 44 | "See the [search.ipynb](./search.ipynb) notebook for details." 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "## Scrolling\n", 52 | "\n", 53 | "See the [scroll.ipynb](./scroll.ipynb) notebook for details.\n", 54 | "\n", 55 | "💡 It is recommended to use the `SearchAsync` API to traverse the index." 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [] 62 | } 63 | ], 64 | "metadata": { 65 | "kernelspec": { 66 | "display_name": ".NET (C#)", 67 | "language": "C#", 68 | "name": ".net-csharp" 69 | }, 70 | "polyglot_notebook": { 71 | "kernelInfo": { 72 | "defaultKernelName": "csharp", 73 | "items": [ 74 | { 75 | "aliases": [], 76 | "languageName": "csharp", 77 | "name": "csharp" 78 | } 79 | ] 80 | } 81 | } 82 | }, 83 | "nbformat": 4, 84 | "nbformat_minor": 2 85 | } 86 | -------------------------------------------------------------------------------- /src/nest-vs-elasticsearch/scroll.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 16, 6 | "metadata": { 7 | "dotnet_interactive": { 8 | "language": "csharp" 9 | }, 10 | "polyglot_notebook": { 11 | "kernelName": "csharp" 12 | } 13 | }, 14 | "outputs": [], 15 | "source": [ 16 | "#!import ./setup-clients.ipynb\n", 17 | "\n", 18 | "const string PostsIndex = \"posts\";" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "## Srolling - `Elastic.Clients.Elasticsearch`\n", 26 | "\n", 27 | "\n" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 31, 33 | "metadata": { 34 | "dotnet_interactive": { 35 | "language": "csharp" 36 | }, 37 | "polyglot_notebook": { 38 | "kernelName": "csharp" 39 | } 40 | }, 41 | "outputs": [], 42 | "source": [ 43 | "#nullable enable\n", 44 | "using Elastic.Clients.Elasticsearch;\n", 45 | "\n", 46 | "public class ScrollConfiguration\n", 47 | "{\n", 48 | " public int PageSize { get; set; } = 250;\n", 49 | "\n", 50 | " public Elastic.Clients.Elasticsearch.Duration KeepAlive { get; set; } = new Elastic.Clients.Elasticsearch.Duration(\"1m\");\n", 51 | "\n", 52 | " public bool DebugOutput { get; set; } = false;\n", 53 | "\n", 54 | " public static ScrollConfiguration Default => new ();\n", 55 | "}\n", 56 | "\n", 57 | "public static class ElasticsearchClientExtensions\n", 58 | "{\n", 59 | " public static async IAsyncEnumerable ScrollAsync(\n", 60 | " ElasticsearchClient client,\n", 61 | " Elastic.Clients.Elasticsearch.Indices indices,\n", 62 | " Action>? configure = default, \n", 63 | " ScrollConfiguration? scrollConfiguration = default,\n", 64 | " [System.Runtime.CompilerServices.EnumeratorCancellation] System.Threading.CancellationToken cancellationToken = default)\n", 65 | " {\n", 66 | " Elastic.Clients.Elasticsearch.OpenPointInTimeResponse? pit = default;\n", 67 | "\n", 68 | " scrollConfiguration ??= ScrollConfiguration.Default;\n", 69 | "\n", 70 | " try\n", 71 | " {\n", 72 | " pit = await client.OpenPointInTimeAsync(\n", 73 | " indices,\n", 74 | " d => d.KeepAlive(scrollConfiguration!.KeepAlive),\n", 75 | " cancellationToken\n", 76 | " );\n", 77 | "\n", 78 | " EnsureResponseHasNoErrors(pit);\n", 79 | "\n", 80 | " var hasDocumentsToSearch = false;\n", 81 | " ICollection? searchAfter = null;\n", 82 | "\n", 83 | " do\n", 84 | " {\n", 85 | " var searchResponse = await client.SearchAsync(\n", 86 | " s =>\n", 87 | " {\n", 88 | " ConfigureSearch(s);\n", 89 | "\n", 90 | " if (searchAfter is not null)\n", 91 | " {\n", 92 | " s.SearchAfter(searchAfter);\n", 93 | " }\n", 94 | " },\n", 95 | " cancellationToken\n", 96 | " );\n", 97 | "\n", 98 | " EnsureResponseHasNoErrors(searchResponse);\n", 99 | "\n", 100 | " foreach (var document in searchResponse.Documents)\n", 101 | " {\n", 102 | " yield return document;\n", 103 | " }\n", 104 | "\n", 105 | " searchAfter = searchResponse.Hits.LastOrDefault()?.Sort?.ToArray();\n", 106 | " hasDocumentsToSearch = searchAfter is { Count: > 0 };\n", 107 | " } while (hasDocumentsToSearch);\n", 108 | " }\n", 109 | " finally\n", 110 | " {\n", 111 | " if (pit is not null)\n", 112 | " {\n", 113 | " var closeResponse = await client.ClosePointInTimeAsync(\n", 114 | " d => d.Id(pit.Id),\n", 115 | " cancellationToken\n", 116 | " );\n", 117 | " EnsureResponseHasNoErrors(closeResponse);\n", 118 | " }\n", 119 | " }\n", 120 | "\n", 121 | " SearchRequestDescriptor ConfigureSearch(SearchRequestDescriptor descriptor)\n", 122 | " {\n", 123 | " descriptor = descriptor\n", 124 | " .Index(indices)\n", 125 | " .Pit(pit!.Id, d => d.KeepAlive(scrollConfiguration!.KeepAlive))\n", 126 | " .Size(scrollConfiguration.PageSize);\n", 127 | "\n", 128 | " if(configure is not null)\n", 129 | " {\n", 130 | " configure(descriptor);\n", 131 | " }\n", 132 | " else\n", 133 | " {\n", 134 | " descriptor.Sort(\n", 135 | " [\n", 136 | " SortOptions.Field(\n", 137 | " \"_doc\",\n", 138 | " new Elastic.Clients.Elasticsearch.FieldSort { Order = Elastic.Clients.Elasticsearch.SortOrder.Asc })\n", 139 | " ]);\n", 140 | " }\n", 141 | "\n", 142 | " return descriptor;\n", 143 | " }\n", 144 | "\n", 145 | " void EnsureResponseHasNoErrors(ElasticsearchResponse response)\n", 146 | " {\n", 147 | " if (!response.IsSuccess() && response.TryGetOriginalException(out var ex))\n", 148 | " {\n", 149 | " throw ex!;\n", 150 | " }\n", 151 | " }\n", 152 | " }\n", 153 | "}" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": 32, 159 | "metadata": { 160 | "dotnet_interactive": { 161 | "language": "csharp" 162 | }, 163 | "polyglot_notebook": { 164 | "kernelName": "csharp" 165 | } 166 | }, 167 | "outputs": [], 168 | "source": [ 169 | "var scrollConfiguration = ScrollConfiguration.Default;\n", 170 | "scrollConfiguration.PageSize = 500;\n", 171 | "scrollConfiguration.DebugOutput = false;\n", 172 | "\n", 173 | "var source = ElasticsearchClientExtensions.ScrollAsync(client, PostsIndex, scrollConfiguration: scrollConfiguration);\n", 174 | "\n", 175 | "display(await source.CountAsync());\n", 176 | "// var posts = await source.ToListAsync();" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": null, 182 | "metadata": { 183 | "dotnet_interactive": { 184 | "language": "csharp" 185 | }, 186 | "polyglot_notebook": { 187 | "kernelName": "csharp" 188 | } 189 | }, 190 | "outputs": [], 191 | "source": [] 192 | } 193 | ], 194 | "metadata": { 195 | "kernelspec": { 196 | "display_name": ".NET (C#)", 197 | "language": "C#", 198 | "name": ".net-csharp" 199 | }, 200 | "polyglot_notebook": { 201 | "kernelInfo": { 202 | "defaultKernelName": "csharp", 203 | "items": [ 204 | { 205 | "aliases": [], 206 | "languageName": "csharp", 207 | "name": "csharp" 208 | } 209 | ] 210 | } 211 | } 212 | }, 213 | "nbformat": 4, 214 | "nbformat_minor": 2 215 | } 216 | -------------------------------------------------------------------------------- /src/nest-vs-elasticsearch/search.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "dotnet_interactive": { 8 | "language": "csharp" 9 | }, 10 | "polyglot_notebook": { 11 | "kernelName": "csharp" 12 | } 13 | }, 14 | "outputs": [], 15 | "source": [ 16 | "const string PostsIndex = \"posts\";\n", 17 | "\n", 18 | "#!import ./setup-clients.ipynb" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "# Searching for data" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 3, 31 | "metadata": { 32 | "dotnet_interactive": { 33 | "language": "csharp" 34 | }, 35 | "polyglot_notebook": { 36 | "kernelName": "csharp" 37 | } 38 | }, 39 | "outputs": [], 40 | "source": [ 41 | "var testCaseName = \"search-all\";\n", 42 | "\n", 43 | "// Nest\n", 44 | "var _response = await _client.SearchAsync(s => s\n", 45 | " .Index(PostsIndex).MatchAll()\n", 46 | ");\n", 47 | "\n", 48 | "\n", 49 | "// Elastic.Clients.Elasticsearch\n", 50 | "var response = await client.SearchAsync(s => s \n", 51 | " .Index(PostsIndex).Query(q => q.MatchAll(q => {}))\n", 52 | ");\n", 53 | "\n", 54 | "ReportAndAssert(testCaseName, _response, response);" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 4, 60 | "metadata": { 61 | "dotnet_interactive": { 62 | "language": "csharp" 63 | }, 64 | "polyglot_notebook": { 65 | "kernelName": "csharp" 66 | } 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "var testCaseName = \"simple-search-with-term-and-pagination\";\n", 71 | "\n", 72 | "var _response = await _client.SearchAsync(s => s\n", 73 | " .Index(PostsIndex)\n", 74 | " .From(0)\n", 75 | " .Size(10)\n", 76 | " .Query(q => q\n", 77 | " .Term(t => t.Field(f=> f.Title).Value(\"Generic Fresh Cheese\"))\n", 78 | " )\n", 79 | ");\n", 80 | "\n", 81 | "var response = await client.SearchAsync(s => s \n", 82 | " .Index(PostsIndex)\n", 83 | " .From(0)\n", 84 | " .Size(10)\n", 85 | " .Query(q => q\n", 86 | " .Term(t => t.Field(f => f.Title).Value(\"Generic Fresh Cheese\")) \n", 87 | " )\n", 88 | ");\n", 89 | "\n", 90 | "ReportAndAssert(testCaseName, _response, response);" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": { 97 | "dotnet_interactive": { 98 | "language": "csharp" 99 | }, 100 | "polyglot_notebook": { 101 | "kernelName": "csharp" 102 | } 103 | }, 104 | "outputs": [], 105 | "source": [] 106 | } 107 | ], 108 | "metadata": { 109 | "kernelspec": { 110 | "display_name": ".NET (C#)", 111 | "language": "C#", 112 | "name": ".net-csharp" 113 | }, 114 | "polyglot_notebook": { 115 | "kernelInfo": { 116 | "defaultKernelName": "csharp", 117 | "items": [ 118 | { 119 | "aliases": [], 120 | "languageName": "csharp", 121 | "name": "csharp" 122 | } 123 | ] 124 | } 125 | } 126 | }, 127 | "nbformat": 4, 128 | "nbformat_minor": 2 129 | } 130 | -------------------------------------------------------------------------------- /src/nest-vs-elasticsearch/setup-clients.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 3, 6 | "metadata": { 7 | "dotnet_interactive": { 8 | "language": "csharp" 9 | }, 10 | "polyglot_notebook": { 11 | "kernelName": "csharp" 12 | } 13 | }, 14 | "outputs": [], 15 | "source": [ 16 | "#!import ../_infra/get-connection-string.ipynb\n", 17 | "#!import ./common.ipynb\n", 18 | "\n", 19 | "using Nest;\n", 20 | "using Elastic.Transport;\n", 21 | "using Elastic.Clients.Elasticsearch;\n", 22 | "using Elastic.Transport.Products.Elasticsearch;\n", 23 | "\n", 24 | "var connectionString = new Uri(connectionStringInput);\n", 25 | "var nestClientSettings = new ConnectionSettings(connectionString)\n", 26 | " .DisableDirectStreaming()\n", 27 | " .ServerCertificateValidationCallback(CertificateValidations.AllowAll);\n", 28 | "var elasticSettings = new ElasticsearchClientSettings(connectionString)\n", 29 | " .DisableDirectStreaming()\n", 30 | " .ServerCertificateValidationCallback(CertificateValidations.AllowAll);\n", 31 | "\n", 32 | "var _client = new ElasticClient(nestClientSettings);\n", 33 | "var client = new ElasticsearchClient(elasticSettings);" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": { 40 | "dotnet_interactive": { 41 | "language": "csharp" 42 | }, 43 | "polyglot_notebook": { 44 | "kernelName": "csharp" 45 | } 46 | }, 47 | "outputs": [], 48 | "source": [] 49 | } 50 | ], 51 | "metadata": { 52 | "kernelspec": { 53 | "display_name": ".NET (C#)", 54 | "language": "C#", 55 | "name": ".net-csharp" 56 | }, 57 | "polyglot_notebook": { 58 | "kernelInfo": { 59 | "defaultKernelName": "csharp", 60 | "items": [ 61 | { 62 | "aliases": [], 63 | "languageName": "csharp", 64 | "name": "csharp" 65 | } 66 | ] 67 | } 68 | } 69 | }, 70 | "nbformat": 4, 71 | "nbformat_minor": 2 72 | } 73 | -------------------------------------------------------------------------------- /src/nest-vs-elasticsearch/setup-elastic-net.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 3, 6 | "metadata": { 7 | "dotnet_interactive": { 8 | "language": "csharp" 9 | }, 10 | "polyglot_notebook": { 11 | "kernelName": "csharp" 12 | } 13 | }, 14 | "outputs": [], 15 | "source": [ 16 | "#!import ./get-connection-string.ipynb" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "# Index Management" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "### Create Index" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 2, 36 | "metadata": { 37 | "dotnet_interactive": { 38 | "language": "csharp" 39 | }, 40 | "polyglot_notebook": { 41 | "kernelName": "csharp" 42 | } 43 | }, 44 | "outputs": [], 45 | "source": [ 46 | "using Elastic.Clients.Elasticsearch.IndexManagement;\n", 47 | "using Elastic.Clients.Elasticsearch;\n", 48 | "using Elastic.Transport;\n", 49 | "\n", 50 | "var indexName = Elastic.Clients.Elasticsearch.IndexName.From();\n", 51 | "\n", 52 | "var elasticSettings = new ElasticsearchClientSettings(connectionString)\n", 53 | " .DisableDirectStreaming()\n", 54 | " .ServerCertificateValidationCallback(CertificateValidations.AllowAll);\n", 55 | "\n", 56 | "elasticSettings.DefaultMappingFor(s => s.IndexName(\"posts\"));\n", 57 | "\n", 58 | "var client = new ElasticsearchClient(elasticSettings);\n" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 25, 64 | "metadata": { 65 | "dotnet_interactive": { 66 | "language": "csharp" 67 | }, 68 | "polyglot_notebook": { 69 | "kernelName": "csharp" 70 | } 71 | }, 72 | "outputs": [], 73 | "source": [ 74 | "using Elastic.Clients.Elasticsearch.Mapping;\n", 75 | "using Infer = Elastic.Clients.Elasticsearch.Infer;\n", 76 | "\n", 77 | "var descriptor = new Elastic.Clients.Elasticsearch.IndexManagement.CreateIndexRequestDescriptor(indexName)\n", 78 | " .Mappings(md => md.Properties(ApplyBlogPostMapping));\n", 79 | "\n", 80 | "var createResponse = await client.Indices.CreateAsync(descriptor);\n", 81 | "DumpResponse(createResponse);\n", 82 | "\n", 83 | "void ApplyBlogPostMapping(\n", 84 | " Elastic.Clients.Elasticsearch.Mapping.PropertiesDescriptor descriptor)\n", 85 | "{\n", 86 | " descriptor\n", 87 | " .Keyword(p => p.Id)\n", 88 | " .Text(p => p.Title)\n", 89 | " .Text(p => p.Description)\n", 90 | " .Date(p => p.CreatedAt)\n", 91 | " .Boolean(p => p.IsPublished)\n", 92 | " .Object(p => p.Author, d => d.Properties(\n", 93 | " pp => pp\n", 94 | " .Keyword(p => p.Author.Id)\n", 95 | " .Text(p => p.Author.Name)\n", 96 | " .Text(p => p.Author.Email)\n", 97 | " )\n", 98 | " )\n", 99 | " .Object(p => p.Likes, d => d.Properties(\n", 100 | " pp => pp\n", 101 | " .Keyword(Infer.Property(p => p.Id))\n", 102 | " .Keyword(Infer.Property(p => p.PostId))\n", 103 | " .Text(Infer.Property(p => p.AuthorId))\n", 104 | " .Date(Infer.Property(p => p.CreatedAt))\n", 105 | " )\n", 106 | " );\n", 107 | "}" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "### Delete Index" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 9, 120 | "metadata": { 121 | "dotnet_interactive": { 122 | "language": "csharp" 123 | }, 124 | "polyglot_notebook": { 125 | "kernelName": "csharp" 126 | } 127 | }, 128 | "outputs": [], 129 | "source": [ 130 | "var deleteResponse = await client.Indices.DeleteAsync();\n", 131 | "\n", 132 | "DumpResponse(deleteResponse);" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "### Seed Data" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 4, 145 | "metadata": { 146 | "dotnet_interactive": { 147 | "language": "csharp" 148 | }, 149 | "polyglot_notebook": { 150 | "kernelName": "csharp" 151 | } 152 | }, 153 | "outputs": [], 154 | "source": [ 155 | "Bogus.Randomizer.Seed = new Random(123);\n", 156 | "\n", 157 | "var faker = new Bogus.Faker()\n", 158 | " .RuleFor(p => p.Id, f => f.Random.Number(int.MaxValue))\n", 159 | " .RuleFor(p => p.Title, f => f.Commerce.ProductName())\n", 160 | " .RuleFor(p => p.Description, f => f.Lorem.Paragraph())\n", 161 | " .RuleFor(p => p.Author, f => f.Name.FullName())\n", 162 | " .RuleFor(p => p.CreatedAt, f => f.Date.Past());\n", 163 | "\n", 164 | "var posts = faker.Generate(1000);" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 5, 170 | "metadata": { 171 | "dotnet_interactive": { 172 | "language": "csharp" 173 | }, 174 | "polyglot_notebook": { 175 | "kernelName": "csharp" 176 | } 177 | }, 178 | "outputs": [], 179 | "source": [ 180 | "var bulkResponse = await _client.BulkAsync(b => b\n", 181 | " .Index(PostsIndex)\n", 182 | " .IndexMany(posts)\n", 183 | ");\n", 184 | "\n", 185 | "display(bulkResponse.Errors);" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": 28, 191 | "metadata": { 192 | "dotnet_interactive": { 193 | "language": "csharp" 194 | }, 195 | "polyglot_notebook": { 196 | "kernelName": "csharp" 197 | } 198 | }, 199 | "outputs": [], 200 | "source": [ 201 | "DumpResponse(bulkResponse);" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": null, 207 | "metadata": { 208 | "dotnet_interactive": { 209 | "language": "csharp" 210 | }, 211 | "polyglot_notebook": { 212 | "kernelName": "csharp" 213 | } 214 | }, 215 | "outputs": [], 216 | "source": [] 217 | } 218 | ], 219 | "metadata": { 220 | "kernelspec": { 221 | "display_name": ".NET (C#)", 222 | "language": "C#", 223 | "name": ".net-csharp" 224 | }, 225 | "polyglot_notebook": { 226 | "kernelInfo": { 227 | "defaultKernelName": "csharp", 228 | "items": [ 229 | { 230 | "aliases": [], 231 | "languageName": "csharp", 232 | "name": "csharp" 233 | } 234 | ] 235 | } 236 | } 237 | }, 238 | "nbformat": 4, 239 | "nbformat_minor": 2 240 | } 241 | -------------------------------------------------------------------------------- /src/nest-vs-elasticsearch/setup-nest.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 3, 6 | "metadata": { 7 | "dotnet_interactive": { 8 | "language": "csharp" 9 | }, 10 | "polyglot_notebook": { 11 | "kernelName": "csharp" 12 | } 13 | }, 14 | "outputs": [], 15 | "source": [ 16 | "const string PostsIndex = \"posts\";\n", 17 | "\n", 18 | "#!import ./setup-clients.ipynb" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "# Index Management" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "### Create Index" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 2, 38 | "metadata": { 39 | "dotnet_interactive": { 40 | "language": "csharp" 41 | }, 42 | "polyglot_notebook": { 43 | "kernelName": "csharp" 44 | } 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "var createResponse = await _client.Indices.CreateAsync(\n", 49 | " PostsIndex, c => c.Map(m => m.AutoMap()));\n", 50 | "\n", 51 | "DumpResponse(createResponse);" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "### Get Index Info" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 3, 64 | "metadata": { 65 | "dotnet_interactive": { 66 | "language": "csharp" 67 | }, 68 | "polyglot_notebook": { 69 | "kernelName": "csharp" 70 | } 71 | }, 72 | "outputs": [], 73 | "source": [ 74 | "var dataIndexResponse = await client.Indices.GetAsync(PostsIndex);\n", 75 | "var dataIndex = dataIndexResponse.Indices.First().Value;\n", 76 | "\n", 77 | "DumpResponse(dataIndexResponse);\n", 78 | "// display(dataIndex.Mappings.Properties);" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "### Delete Index" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 9, 91 | "metadata": { 92 | "dotnet_interactive": { 93 | "language": "csharp" 94 | }, 95 | "polyglot_notebook": { 96 | "kernelName": "csharp" 97 | } 98 | }, 99 | "outputs": [], 100 | "source": [ 101 | "var response = await client.Indices.DeleteAsync(PostsIndex);\n", 102 | "DumpResponse(response);" 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": {}, 108 | "source": [ 109 | "### Seed Data" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 4, 115 | "metadata": { 116 | "dotnet_interactive": { 117 | "language": "csharp" 118 | }, 119 | "polyglot_notebook": { 120 | "kernelName": "csharp" 121 | } 122 | }, 123 | "outputs": [], 124 | "source": [ 125 | "Bogus.Randomizer.Seed = new Random(123);\n", 126 | "\n", 127 | "var faker = new Bogus.Faker()\n", 128 | " .RuleFor(p => p.Id, f => f.Random.Number(int.MaxValue))\n", 129 | " .RuleFor(p => p.Title, f => f.Commerce.ProductName())\n", 130 | " .RuleFor(p => p.Description, f => f.Lorem.Paragraph())\n", 131 | " .RuleFor(p => p.Author, f => f.Name.FullName())\n", 132 | " .RuleFor(p => p.CreatedAt, f => f.Date.Past());\n", 133 | "\n", 134 | "var posts = faker.Generate(1000);" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": null, 140 | "metadata": { 141 | "dotnet_interactive": { 142 | "language": "csharp" 143 | }, 144 | "polyglot_notebook": { 145 | "kernelName": "csharp" 146 | } 147 | }, 148 | "outputs": [], 149 | "source": [ 150 | "using Microsoft.Data.Analysis;\n", 151 | "\n", 152 | "var ids = new Int32DataFrameColumn(\"Id\", posts.Select(p => p.Id));\n", 153 | "var createdAt = new DateTimeDataFrameColumn(\"CreatedAt\", posts.Select(p => p.CreatedAt));\n", 154 | "var titles = new StringDataFrameColumn(\"Title\", posts.Select(p => p.Title));\n", 155 | "var descriptions = new StringDataFrameColumn(\"Description\", posts.Select(p => p.Description));\n", 156 | "var authors = new StringDataFrameColumn(\"Author\", posts.Select(p => p.Author));\n", 157 | "var dataFrame = new DataFrame(ids, createdAt, titles, descriptions, authors);\n", 158 | "\n", 159 | "display(dataFrame);" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 5, 165 | "metadata": { 166 | "dotnet_interactive": { 167 | "language": "csharp" 168 | }, 169 | "polyglot_notebook": { 170 | "kernelName": "csharp" 171 | } 172 | }, 173 | "outputs": [], 174 | "source": [ 175 | "var bulkResponse = await _client.BulkAsync(b => b\n", 176 | " .Index(PostsIndex)\n", 177 | " .IndexMany(posts)\n", 178 | ");\n", 179 | "\n", 180 | "display(bulkResponse.Errors);" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": { 187 | "dotnet_interactive": { 188 | "language": "csharp" 189 | }, 190 | "polyglot_notebook": { 191 | "kernelName": "csharp" 192 | } 193 | }, 194 | "outputs": [], 195 | "source": [ 196 | "DumpResponse(bulkResponse);" 197 | ] 198 | } 199 | ], 200 | "metadata": { 201 | "kernelspec": { 202 | "display_name": ".NET (C#)", 203 | "language": "C#", 204 | "name": ".net-csharp" 205 | }, 206 | "polyglot_notebook": { 207 | "kernelInfo": { 208 | "defaultKernelName": "csharp", 209 | "items": [ 210 | { 211 | "aliases": [], 212 | "languageName": "csharp", 213 | "name": "csharp" 214 | } 215 | ] 216 | } 217 | } 218 | }, 219 | "nbformat": 4, 220 | "nbformat_minor": 2 221 | } 222 | --------------------------------------------------------------------------------