├── .azuredevops ├── epinova-nuget-steps.md └── epinova-nuget-steps.yml ├── .editorconfig ├── .gitignore ├── Directory.Build.props ├── Epinova.ElasticSearch.sln ├── LICENSE ├── assets ├── bestbets.png ├── index-admin.png ├── mltcomp.png ├── tools-button.png ├── tracking.PNG └── tree-button.png ├── azure-pipelines.yml ├── changelog.md ├── cover.txt ├── lib └── MSBuildCommunityTasks │ ├── MSBuild.Community.Tasks.Targets │ ├── MSBuild.Community.Tasks.dll │ ├── MSBuild.Community.Tasks.pdb │ ├── MSBuild.Community.Tasks.xml │ └── MSBuild.Community.Tasks.xsd ├── nuget.config ├── readme.md ├── setup.md ├── src ├── Epinova.ElasticSearch.Core.EPiServer.Commerce │ ├── CatalogSearchHit.cs │ ├── CatalogSearchResult.cs │ ├── Controllers │ │ ├── ElasticAdminCommerceController.cs │ │ └── ElasticBestBetsCommerceController.cs │ ├── Epinova.ElasticSearch.Core.EPiServer.Commerce.csproj │ ├── Extensions │ │ └── CatalogExtensions.cs │ ├── Plugin │ │ └── IndexEpiserverCommerceContent.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── Providers │ │ ├── CampaignSearchProvider.cs │ │ └── ProductSearchProvider.cs ├── Epinova.ElasticSearch.Core.EPiServer │ ├── Components │ │ └── MoreLikeThis │ │ │ ├── MoreLikeThisComponent.cs │ │ │ └── MoreLikeThisQuery.cs │ ├── ContentSearchHit.cs │ ├── ContentSearchResult.cs │ ├── Contracts │ │ ├── IAutoSuggestRepository.cs │ │ ├── IContentIndexService.cs │ │ ├── IIndexer.cs │ │ └── ISynonymRepository.cs │ ├── Controllers │ │ ├── Abstractions │ │ │ └── ElasticSearchControllerBase.cs │ │ ├── ElasticAdminController.cs │ │ ├── ElasticAutoSuggestController.cs │ │ ├── ElasticBestBetsController.cs │ │ ├── ElasticBoostingController.cs │ │ ├── ElasticConsoleController.cs │ │ ├── ElasticIndexInspectorController.cs │ │ ├── ElasticIndexerController.cs │ │ ├── ElasticMappingController.cs │ │ ├── ElasticSettingsController.cs │ │ ├── ElasticSynonymsController.cs │ │ └── ElasticTrackingController.cs │ ├── Enums │ │ └── IndexingStatus.cs │ ├── Epinova.ElasticSearch.Core.EPiServer.csproj │ ├── EpiserverSearchLanguage.cs │ ├── Events │ │ └── IndexingEvents.cs │ ├── Extensions │ │ ├── AdminStringExtensions.cs │ │ ├── ContentExtensions.cs │ │ ├── CustomResultExtensions.cs │ │ ├── IndexingExtensions.cs │ │ ├── LocalizationExtensions.cs │ │ ├── QueryExtensions.cs │ │ └── XhtmlStringExtensions.cs │ ├── Indexer.cs │ ├── Initialization │ │ ├── IndexInitializer.cs │ │ ├── IndexingInitializer.cs │ │ ├── LocalizationInitialization.cs │ │ ├── SearchLanguageInitializer.cs │ │ ├── TrackingInitializer.cs │ │ └── ViewEngineInitializer.cs │ ├── Install.ps1 │ ├── Lang │ │ ├── Epinova.ElasticSearchEN.xml │ │ └── Epinova.ElasticSearchNO.xml │ ├── Models │ │ ├── AutoSuggestFile.cs │ │ ├── AutoSuggestFileFolder.cs │ │ ├── BestBetsFile.cs │ │ ├── BestBetsFileFolder.cs │ │ ├── BoostItem.cs │ │ ├── BoostingData.cs │ │ ├── BoostingFolder.cs │ │ ├── BoostingInputModel.cs │ │ ├── BoostingViewModel.cs │ │ ├── Synonym.cs │ │ ├── SynonymBackupFile.cs │ │ ├── SynonymBackupFileFolder.cs │ │ └── ViewModels │ │ │ ├── Abstractions │ │ │ ├── LanguageAwareViewModelBase.cs │ │ │ └── LanguageViewModelBase.cs │ │ │ ├── AdminViewModel.cs │ │ │ ├── AutoSuggestViewModel.cs │ │ │ ├── BestBetsByLanguage.cs │ │ │ ├── BestBetsViewModel.cs │ │ │ ├── ConsoleViewModel.cs │ │ │ ├── InspectLanguage.cs │ │ │ ├── InspectViewModel.cs │ │ │ ├── LanguageAutoSuggestWords.cs │ │ │ ├── LanguageSynonyms.cs │ │ │ ├── MappingViewModel.cs │ │ │ ├── SettingsViewModel.cs │ │ │ ├── SynonymsViewModel.cs │ │ │ ├── TrackingByLanguage.cs │ │ │ ├── TrackingLanguage.cs │ │ │ └── TrackingViewModel.cs │ ├── Plugin │ │ └── IndexEPiServerContent.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Providers │ │ ├── BlockSearchProvider.cs │ │ ├── ElasticSearchMenuProvider.cs │ │ ├── FileSearchProvider.cs │ │ ├── PageSearchProvider.cs │ │ ├── ProviderConstants.cs │ │ └── SearchProviderBase.cs │ ├── RoleNames.cs │ ├── Services │ │ ├── AutoSuggestRepository.cs │ │ ├── BestBetsRepository.cs │ │ ├── BoostingRepository.cs │ │ ├── ContentIndexService.cs │ │ ├── InspectorRepository.cs │ │ └── SynonymRepository.cs │ ├── Views │ │ ├── ElasticSearchAdmin │ │ │ ├── Admin │ │ │ │ ├── Index.cshtml │ │ │ │ └── Index.generated.cs │ │ │ ├── AutoSuggest │ │ │ │ ├── Index.cshtml │ │ │ │ └── Index.generated.cs │ │ │ ├── BestBets │ │ │ │ ├── Index.cshtml │ │ │ │ └── Index.generated.cs │ │ │ ├── Boosting │ │ │ │ ├── Index.cshtml │ │ │ │ └── Index.generated.cs │ │ │ ├── Console │ │ │ │ ├── Index.cshtml │ │ │ │ └── Index.generated.cs │ │ │ ├── IndexInspector │ │ │ │ ├── Index.cshtml │ │ │ │ └── Index.generated.cs │ │ │ ├── Mapping │ │ │ │ ├── Index.cshtml │ │ │ │ └── Index.generated.cs │ │ │ ├── Settings │ │ │ │ ├── Index.cshtml │ │ │ │ └── Index.generated.cs │ │ │ ├── Synonyms │ │ │ │ ├── Index.cshtml │ │ │ │ └── Index.generated.cs │ │ │ ├── Tracking │ │ │ │ ├── Index.cshtml │ │ │ │ └── Index.generated.cs │ │ │ ├── _ElasticSearch.cshtml │ │ │ └── _ElasticSearch.generated.cs │ │ └── Web.config │ └── modules │ │ └── _protected │ │ ├── ElasticSearch │ │ ├── ClientResources │ │ │ └── Scripts │ │ │ │ ├── MoreLikeThisSearch.js │ │ │ │ ├── ToolbarProvider.js │ │ │ │ ├── UpdateIndexCommand.js │ │ │ │ ├── UpdateTreeStructureCommand.js │ │ │ │ └── init.js │ │ ├── ElasticSearch.zip │ │ └── module.config │ │ └── web.config └── Epinova.ElasticSearch.Core │ ├── Admin │ ├── Health.cs │ └── Index.cs │ ├── Attributes │ ├── BoostAttribute.cs │ ├── DidYouMeanSourceAttribute.cs │ ├── ExcludeFromSearchAttribute.cs │ └── StemAttribute.cs │ ├── Constants.cs │ ├── Contracts │ ├── IBestBetsRepository.cs │ ├── IBoostingRepository.cs │ ├── ICoreIndexer.cs │ ├── IElasticSearchService.cs │ ├── IElasticSearchServiceFilters.cs │ ├── IElasticSearchService{T}.cs │ ├── IFilterGroup.cs │ ├── IHttpClientHelper.cs │ ├── IInspectorRepository.cs │ ├── IProperty.cs │ ├── ISearchLanguage.cs │ ├── IServerInfoService.cs │ └── ITrackingRepository.cs │ ├── Conventions │ ├── BestBet.cs │ ├── CustomProperty.cs │ ├── CustomPropertyConvention.cs │ ├── Indexing.BestBets.cs │ ├── Indexing.CustomProperties.cs │ ├── Indexing.Files.cs │ ├── Indexing.Highlighting.cs │ ├── Indexing.Roots.cs │ ├── Indexing.Suggestions.cs │ ├── Indexing.Types.cs │ ├── Indexing.cs │ ├── MessageHandler.cs │ ├── MoreLikeThis.cs │ └── Suggestion.cs │ ├── CoreIndexer.cs │ ├── DefaultFields.cs │ ├── DefaultSearchLanguage.cs │ ├── ElasticSearchService.GeoPoint.cs │ ├── ElasticSearchService.Range.cs │ ├── ElasticSearchService.cs │ ├── ElasticSearchService{T}.cs │ ├── Engine │ ├── QueryBuilder.cs │ └── SearchEngine.cs │ ├── Enums │ ├── MappingConflict.cs │ ├── MappingType.cs │ ├── Operator.cs │ └── SimpleQuerystringOperators.cs │ ├── Epinova.ElasticSearch.Core.csproj │ ├── Events │ ├── BestBetEventArgs.cs │ ├── Events.cs │ └── IndexItemEventArgs.cs │ ├── Extensions │ ├── EnumExtensions.cs │ ├── SimpleSearchExtensions.cs │ ├── TypeExtensions.cs │ └── UrlExtensions.cs │ ├── FilterGroup.cs │ ├── Models │ ├── Admin │ │ ├── HealthInformation.cs │ │ ├── IndexInformation.cs │ │ ├── Node.cs │ │ ├── Plugin.cs │ │ └── ServerInfo.cs │ ├── Bulk │ │ ├── BulkBatchResult.cs │ │ ├── BulkMetadata.cs │ │ ├── BulkMetadataBase.cs │ │ ├── BulkOperation.cs │ │ ├── BulkResult.cs │ │ ├── BulkResultItem.cs │ │ ├── BulkResultItemStatus.cs │ │ ├── Error.cs │ │ ├── ErrorRootCause.cs │ │ └── Operation.cs │ ├── Constants.cs │ ├── Converters │ │ ├── AggregationConverter.cs │ │ ├── BucketConverter.cs │ │ ├── BulkMetadataConverter.cs │ │ ├── BulkResultItemConverter.cs │ │ ├── GeoBoundingBoxConverter.cs │ │ ├── GeoDistanceConverter.cs │ │ ├── GeoPointConverter.cs │ │ ├── GeoPolygonConverter.cs │ │ ├── RangeConverter.cs │ │ └── TermConverter.cs │ ├── CustomSearchHit.cs │ ├── CustomSearchResult.cs │ ├── Facet.cs │ ├── FacetEntry.cs │ ├── FacetHit.cs │ ├── Filter.cs │ ├── FilterGroupQuery.cs │ ├── IndexItem.cs │ ├── IndexableProperty.cs │ ├── InspectItem.cs │ ├── JsonNames.cs │ ├── Mapping │ │ ├── IndexMapping.cs │ │ ├── IndexMappingProperty.cs │ │ └── MappingValidator.cs │ ├── Properties │ │ ├── GeoPoint.cs │ │ └── IntegerRange.cs │ ├── Query │ │ ├── AggregationTerms.cs │ │ ├── BestBetsRequest.cs │ │ ├── BoolQuery.cs │ │ ├── Boost.cs │ │ ├── Bucket.cs │ │ ├── Completion.cs │ │ ├── DidYouMeanSuggest.cs │ │ ├── FunctionScoreQuery.cs │ │ ├── Gauss.cs │ │ ├── GeoBoundingBox.cs │ │ ├── GeoDistance.cs │ │ ├── GeoPolygon.cs │ │ ├── GeoSort.cs │ │ ├── Highlight.cs │ │ ├── Match.cs │ │ ├── MatchAll.cs │ │ ├── MatchBase.cs │ │ ├── MatchMulti.cs │ │ ├── MatchSimple.cs │ │ ├── MatchSimpleQueryString.cs │ │ ├── MatchWithBoost.cs │ │ ├── MoreLikeThisQuery.cs │ │ ├── MoreLikeThisRequest.cs │ │ ├── Must.cs │ │ ├── NestedBoolQuery.cs │ │ ├── PostFilter.cs │ │ ├── Query.cs │ │ ├── QueryBase.cs │ │ ├── QueryRequest.cs │ │ ├── Range.cs │ │ ├── RangeBase.cs │ │ ├── ScriptScore.cs │ │ ├── ScriptSort.cs │ │ ├── Sort.cs │ │ ├── SuggestRequest.cs │ │ ├── Suggestions.cs │ │ ├── Term.cs │ │ ├── Terms.cs │ │ └── Wildcard.cs │ ├── QuerySetup.cs │ ├── RawResults.cs │ ├── RequestBase.cs │ ├── SearchHit.cs │ ├── SearchResult.cs │ ├── SearchResultBase.cs │ ├── Serialization │ │ ├── Aggregation.cs │ │ ├── DidYouMeanHit.cs │ │ ├── DidYouMeanOption.cs │ │ ├── EsRootObject.cs │ │ ├── Hit.cs │ │ ├── Hits.cs │ │ ├── IndexStatus.cs │ │ ├── Suggest.cs │ │ ├── SuggestionOption.cs │ │ ├── Suggestions.cs │ │ └── SuggestionsRootObject.cs │ ├── Tracking.cs │ └── TypeCount.cs │ ├── Pipelines │ └── Attachment.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── ServerInfoService.cs │ ├── Services │ ├── MappingValidatorService.cs │ └── TrackingRepository.cs │ ├── Settings │ ├── AssemblySettings.cs │ ├── Configuration │ │ ├── ElasticSearchSection.cs │ │ ├── FileConfiguration.cs │ │ ├── FilesCollection.cs │ │ ├── IndexConfiguration.cs │ │ └── IndicesCollection.cs │ ├── ElasticSearchSettings.cs │ └── IElasticSearchSettings.cs │ ├── Utilities │ ├── Analyzers.cs │ ├── ArrayHelper.cs │ ├── AsyncUtil.cs │ ├── DbHelper.cs │ ├── HttpClientHelper.cs │ ├── Indexing.cs │ ├── Language.cs │ ├── Mapping.cs │ ├── MappingPatterns.cs │ ├── Serialization.cs │ ├── TextUtil.cs │ └── Validation.cs │ └── WellKnownProperties.cs └── tests ├── Core.Episerver.Tests ├── Controllers │ ├── Abstractions │ │ └── ElasticSearchControllerBaseTests.cs │ ├── AutoSuggestControllerTests.cs │ ├── ElasticAdminControllerTests.cs │ ├── IndexerControllerTests.cs │ └── SynonymsControllerTests.cs ├── Core.Episerver.Tests.csproj ├── Events │ └── IndexingEventsTests.cs ├── Extensions │ ├── ContentExtensionsTests.cs │ └── TypeExtensionsTests.cs ├── IndexerTests.cs ├── Models │ └── ViewModels │ │ └── Abstractions │ │ └── LanguageAwareViewModelBaseTests.cs ├── Properties │ └── AssemblyInfo.cs ├── QueryExtensionsTests.cs ├── ServiceLocatiorCollection.cs └── app.config ├── Core.Tests ├── Admin │ └── HealthTests.cs ├── BulkOperationTests.cs ├── Conventions │ ├── IndexingTests.cs │ └── MessageHandlerTests.cs ├── Core.Tests.csproj ├── CoreIndexerTests.cs ├── ElasticSearchServiceTests.cs ├── Engine │ ├── QueryBuilderTests.cs │ └── SearchEngineTests.cs ├── Extensions │ ├── SimpleSearchExtensionsTests.cs │ └── TypeExtensionsTests.cs ├── Models │ ├── Converters │ │ ├── GeoBoundingBoxConverterTests.cs │ │ ├── GeoDistanceConverterTests.cs │ │ ├── GeoPointConverterTests.cs │ │ └── GeoPolygonConverterTests.cs │ └── Properties │ │ └── GeoPointTests.cs ├── Properties │ └── AssemblyInfo.cs ├── ServerInfoTests.cs ├── ServiceLocatiorCollection.cs ├── Settings │ └── Configuration │ │ └── ElasticsearchSectionTests.cs ├── Utilities │ ├── AnalyzersTests.cs │ ├── ArrayHelperTests.cs │ ├── HttpClientHelperTests.cs │ ├── LanguageTests.cs │ ├── MappingTests.cs │ ├── TextUtilTests.cs │ └── ValidationTests.cs └── app.config └── TestData ├── ComplexType.cs ├── ElasticFixtureSettings.cs ├── Factory.cs ├── FakeIFileUploadElementBlock.cs ├── Inheritance.cs ├── Json ├── Facets_Field_Foo.json ├── Facets_Field_Type.json ├── HealthInfo.json ├── IndicesInfo.json ├── NodeInfo.json ├── PostFilterShouldBool_false.json ├── PostFilterShouldBool_true.json ├── PostFilterShouldDate.json ├── PostFilterShouldDateTime.json ├── PostFilterShouldDouble_-123456.json ├── PostFilterShouldDouble_123456-7.json ├── PostFilterShouldDouble_123456.json ├── PostFilterShouldFloat_-123456.json ├── PostFilterShouldFloat_0.json ├── PostFilterShouldFloat_123456.json ├── PostFilterShouldInt_-2147483648.json ├── PostFilterShouldInt_0.json ├── PostFilterShouldInt_1234.json ├── PostFilterShouldInt_2147483647.json ├── PostFilterShouldLong_-9223372036854775808.json ├── PostFilterShouldLong_0.json ├── PostFilterShouldLong_1234.json ├── PostFilterShouldLong_9223372036854775807.json ├── PostFilterShouldString_Foo.json ├── PostFilterShouldString_LoremIpsumBaconOmgLongStringMkay.json ├── Results_No_Hits.json ├── Results_With_Custom_Properties.json ├── Results_With_Custom_Properties_NullValues.json ├── Results_With_Facets_Only.json ├── Results_With_Hits_And_Facets.json ├── Results_With_Only_Hits.json ├── SearchOfT_Object.json ├── SearchOfT_String.json ├── Search_Term_Foo-Bar.json ├── Search_Term_Foo.json ├── Search_With_Filter_123_Term_Foo.json ├── Search_With_Filter_123_Term_Foo_Bar.json ├── Search_With_Filter_678_Term_Bar.json ├── Settings.json ├── SimpleQuerystring_Term_Foo.json ├── SimpleQuerystring_Term_Foo_Bar_And_Near_Or.json └── Suggestions_2hits.json ├── Media ├── HelloWorld..exe ├── HelloWorld.docx └── HelloWorld.pdf ├── Properties └── AssemblyInfo.cs ├── ServiceLocationMock.cs ├── ServiceLocatorFixture.cs ├── TestBlock.cs ├── TestData.csproj ├── TestMedia.cs ├── TestPage.cs ├── TestableContentFragment.cs ├── TestableSearchEngine.cs ├── TypeWithBoosting.cs ├── TypeWithExcludeAttribute.cs ├── TypeWithHideFromSearchProperty.cs ├── TypeWithoutBoosting.cs └── app.config /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.user 3 | /**/bin 4 | /**/obj 5 | *.nupkg 6 | **/packages/* 7 | !**/packages/build/ 8 | .vs 9 | .idea -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | latest 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Epinova 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 | -------------------------------------------------------------------------------- /assets/bestbets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/assets/bestbets.png -------------------------------------------------------------------------------- /assets/index-admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/assets/index-admin.png -------------------------------------------------------------------------------- /assets/mltcomp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/assets/mltcomp.png -------------------------------------------------------------------------------- /assets/tools-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/assets/tools-button.png -------------------------------------------------------------------------------- /assets/tracking.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/assets/tracking.PNG -------------------------------------------------------------------------------- /assets/tree-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/assets/tree-button.png -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | system.debug: false 3 | major: 11 # Align with Cms/Commerce version 4 | minor: 8 # Up when breaking changes 5 | 6 | name: $(major).$(minor).$(rev:r) #Build.BuildNumber 7 | 8 | trigger: 9 | batch: true 10 | branches: 11 | include: 12 | - main 13 | - release/* 14 | 15 | pool: 16 | vmImage: windows-2019 17 | 18 | steps: 19 | - template: .azuredevops/epinova-nuget-steps.yml 20 | parameters: 21 | dotnetVersions: [] 22 | dotnetTestFilter: FullyQualifiedName!~IntegrationTests 23 | nugetPushServiceConnection: Nuget.org push 24 | 25 | - task: GithubRelease@1 26 | displayName: 'Create GitHub Release' 27 | condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') 28 | inputs: 29 | gitHubConnection: github.com_otanum 30 | repositoryName: Epinova/Epinova.Elasticsearch 31 | tagSource: userSpecifiedTag 32 | tag: v$(Build.BuildNumber) 33 | assets: $(Build.ArtifactStagingDirectory)/*.nupkg 34 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | #### 11.7.3 4 | * Added mapping validator. 5 | 6 | #### 11.7.2 7 | * Best bets content selector 8 | * Create indices with mappings 9 | 10 | #### 11.7.1 11 | * Geo-point filtering and sorting 12 | * Support for `IDictionary` properties. 13 | * Delete-all function in index administration GUI 14 | * Support for synonyms from file 15 | * Support for multiword synonyms 16 | * Support for all blob types (remove hardcoded FileBlob coupling) 17 | * Index unpublished content 18 | 19 | #### 11.7 20 | * ES 7 support 21 | 22 | #### 11.6 23 | * ACL filtering 24 | * ES 6 support 25 | * Replace Attachment Mapper with Ingest plugin 26 | * No dynamic mapping, base on analyzed content 27 | 28 | #### 11.4 29 | 30 | * Commerce overhaul, catalog content now has its own index 31 | * MoreLikeThis function including widget 32 | 33 | #### 11.3 34 | 35 | * Moved to GitHub 36 | * Added support for custom MessageHandler in HttpClient 37 | -------------------------------------------------------------------------------- /cover.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/cover.txt -------------------------------------------------------------------------------- /lib/MSBuildCommunityTasks/MSBuild.Community.Tasks.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/lib/MSBuildCommunityTasks/MSBuild.Community.Tasks.dll -------------------------------------------------------------------------------- /lib/MSBuildCommunityTasks/MSBuild.Community.Tasks.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/lib/MSBuildCommunityTasks/MSBuild.Community.Tasks.pdb -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer.Commerce/CatalogSearchHit.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using EPiServer.Commerce.Catalog.ContentTypes; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Commerce 5 | { 6 | public sealed class CatalogSearchHit where T : EntryContentBase 7 | { 8 | public CatalogSearchHit(T content, Dictionary custom, double queryScore, string highlight) 9 | { 10 | Content = content; 11 | Custom = custom; 12 | Score = queryScore; 13 | Highlight = highlight; 14 | } 15 | 16 | public T Content { get; } 17 | 18 | public Dictionary Custom { get; } 19 | 20 | public double Score { get; } 21 | 22 | public string Highlight { get; } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer.Commerce/CatalogSearchResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Epinova.ElasticSearch.Core.Models; 4 | using EPiServer.Commerce.Catalog.ContentTypes; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Commerce 7 | { 8 | /// 9 | /// Contains the materialization of the search query 10 | /// 11 | public sealed class CatalogSearchResult : SearchResultBase> 12 | where T : EntryContentBase 13 | { 14 | public CatalogSearchResult(SearchResult searchResult, IEnumerable> filteredHits) 15 | { 16 | Query = searchResult.Query; 17 | Took = searchResult.Took; 18 | Facets = searchResult.Facets; 19 | Hits = filteredHits ?? Enumerable.Empty>(); 20 | TotalHits = searchResult.TotalHits; 21 | DidYouMeanSuggestions = searchResult.DidYouMeanSuggestions; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer.Commerce/Epinova.ElasticSearch.Core.EPiServer.Commerce.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net472 4 | Epinova.ElasticSearch.Core.EPiServer.Commerce 5 | Epinova.ElasticSearch.Core.EPiServer.Commerce 6 | Epinova ElasticSearch Core Commerce functionality $(Gitlog) 7 | $(GitContributors) @ Epinova 8 | Epinova AS 9 | Copyright© 2023 - Epinova AS 10 | false 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer.Commerce/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | [assembly: ComVisible(false)] 4 | [assembly: Guid("f5a0a67a-4a0f-4a91-b54d-b3fcb1410765")] 5 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer.Commerce/Providers/CampaignSearchProvider.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.EPiServer.Providers; 2 | using EPiServer.Core; 3 | using EPiServer.Core.Internal; 4 | using EPiServer.DataAbstraction; 5 | using EPiServer.ServiceLocation; 6 | using EPiServer.Shell.Search; 7 | 8 | namespace Epinova.ElasticSearch.Core.EPiServer.Commerce.Providers 9 | { 10 | [SearchProvider] 11 | public class CampaignSearchProvider : SearchProviderBase 12 | { 13 | #pragma warning disable RCS1170 // Use read-only auto-implemented property. 14 | private static Injected RootService { get; set; } 15 | private static Injected DefaultContentProvider { get; set; } 16 | #pragma warning restore RCS1170 // Use read-only auto-implemented property. 17 | 18 | public CampaignSearchProvider() : base("campaigns") 19 | { 20 | IconClass = ProviderConstants.CommerceCampaignsIconCssClass; 21 | AreaName = ProviderConstants.CommerceCampaignsArea; 22 | ForceRootLookup = true; 23 | } 24 | 25 | protected override string GetSearchRoot() => RootService.Service.Get("SysCampaignRoot").ID.ToString(); 26 | 27 | protected override string[] GetProviderKeys() 28 | { 29 | return new[] 30 | { 31 | ProviderConstants.CatalogProviderKey, 32 | DefaultContentProvider.Service.ProviderKey ?? ProviderConstants.DefaultProviderKey 33 | }; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer.Commerce/Providers/ProductSearchProvider.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.EPiServer.Providers; 2 | using EPiServer.Core; 3 | using EPiServer.Core.Internal; 4 | using EPiServer.DataAbstraction; 5 | using EPiServer.ServiceLocation; 6 | using EPiServer.Shell.Search; 7 | using Mediachase.Commerce.Catalog; 8 | 9 | namespace Epinova.ElasticSearch.Core.EPiServer.Commerce.Providers 10 | { 11 | [SearchProvider] 12 | public class ProductSearchProvider : SearchProviderBase 13 | { 14 | #pragma warning disable RCS1170 // Use read-only auto-implemented property. 15 | private static Injected DefaultContentProvider { get; set; } 16 | private static Injected ReferenceConverter { get; set; } 17 | #pragma warning restore RCS1170 // Use read-only auto-implemented property. 18 | 19 | public ProductSearchProvider() : base("product") 20 | { 21 | IconClass = ProviderConstants.CommerceCatalogIconCssClass; 22 | AreaName = ProviderConstants.CommerceCatalogArea; 23 | ForceRootLookup = true; 24 | IndexName = _elasticSearchSettings.GetCommerceIndexName(SearchLanguage); 25 | } 26 | 27 | protected override string GetSearchRoot() => ReferenceConverter.Service.GetRootLink().ID.ToString(); 28 | 29 | protected override string[] GetProviderKeys() 30 | { 31 | return new[] 32 | { 33 | ProviderConstants.CatalogProviderKey, 34 | DefaultContentProvider.Service.ProviderKey ?? ProviderConstants.DefaultProviderKey 35 | }; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Components/MoreLikeThis/MoreLikeThisComponent.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Shell; 2 | using EPiServer.Shell.ViewComposition; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Components.MoreLikeThis 5 | { 6 | /// 7 | /// Used to perform a MLT query, ref. https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-mlt-query.html 8 | /// Inspired by https://world.episerver.com/blogs/Linus-Ekstrom/Dates/2012/11/Creating-a-component-that-searches-for-content/ 9 | /// 10 | [Component] 11 | public class MoreLikeThisComponent : ComponentDefinitionBase 12 | { 13 | public MoreLikeThisComponent() : base("epinova-elasticsearch/MoreLikeThisSearch") 14 | { 15 | PlugInAreas = new[] { PlugInArea.Assets, "/episerver/commerce/assets" }; 16 | Categories = new[] { "commerce", "cms" }; 17 | LanguagePath = "/epinovaelasticsearch/components/mlt"; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/ContentSearchHit.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using EPiServer.Core; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer 5 | { 6 | public sealed class ContentSearchHit 7 | where T : IContentData 8 | { 9 | public ContentSearchHit(T content, Dictionary custom, double queryScore, string highlight) 10 | { 11 | Content = content; 12 | Custom = custom; 13 | Score = queryScore; 14 | Highlight = highlight; 15 | } 16 | 17 | public T Content { get; } 18 | 19 | public Dictionary Custom { get; } 20 | 21 | public double Score { get; } 22 | 23 | public string Highlight { get; } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/ContentSearchResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Epinova.ElasticSearch.Core.Models; 4 | using EPiServer.Core; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer 7 | { 8 | /// 9 | /// Contains the materialization of the search query 10 | /// 11 | public sealed class ContentSearchResult : SearchResultBase> where T : IContentData 12 | { 13 | public ContentSearchResult(SearchResult searchResult, IEnumerable> filteredHits) 14 | { 15 | Query = searchResult.Query; 16 | Took = searchResult.Took; 17 | Facets = searchResult.Facets; 18 | Hits = filteredHits ?? Enumerable.Empty>(); 19 | TotalHits = searchResult.TotalHits; 20 | DidYouMeanSuggestions = searchResult.DidYouMeanSuggestions; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Contracts/IAutoSuggestRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Epinova.ElasticSearch.Core.EPiServer.Contracts 4 | { 5 | public interface IAutoSuggestRepository 6 | { 7 | void AddWord(string languageId, string word); 8 | void DeleteWord(string languageId, string word); 9 | List GetWords(string languageId); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Contracts/IContentIndexService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using EPiServer.Core; 3 | using EPiServer.DataAbstraction; 4 | 5 | namespace Epinova.ElasticSearch.Core.EPiServer.Contracts 6 | { 7 | public interface IContentIndexService 8 | { 9 | IEnumerable ListContent(List contentReferences, List languages); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Contracts/IIndexer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using Epinova.ElasticSearch.Core.EPiServer.Enums; 5 | using Epinova.ElasticSearch.Core.Models.Bulk; 6 | using EPiServer.Core; 7 | 8 | namespace Epinova.ElasticSearch.Core.EPiServer.Contracts 9 | { 10 | public interface IIndexer 11 | { 12 | BulkBatchResult BulkUpdate(IEnumerable contents, Action logger, string index, int bulkIndex, double bulkCount, string indexingContentType); 13 | void Delete(ContentReference contentLink); 14 | void Delete(IContent content, string indexName = null); 15 | IndexingStatus UpdateStructure(IContent root, string indexName = null); 16 | IndexingStatus Update(IContent content, string indexName = null); 17 | CultureInfo GetLanguage(IContent content); 18 | bool SkipIndexing(IContent content); 19 | bool ShouldHideFromSearch(IContent content); 20 | bool IsExcludedType(IContent content); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Contracts/ISynonymRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.EPiServer.Models; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Contracts 5 | { 6 | public interface ISynonymRepository 7 | { 8 | void SetSynonyms(string languageId, string analyzer, List synonymsToAdd, string index); 9 | List GetSynonyms(string languageId, string index); 10 | string GetSynonymsFilePath(string languageId, string index); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Enums/IndexingStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.EPiServer.Enums 2 | { 3 | public enum IndexingStatus 4 | { 5 | Ok, 6 | ExcludedByConvention, 7 | HideFromSearchProperty, 8 | Error, 9 | PartialError 10 | } 11 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/EpiserverSearchLanguage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.Linq; 4 | using Epinova.ElasticSearch.Core.Contracts; 5 | using EPiServer.DataAbstraction; 6 | using EPiServer.ServiceLocation; 7 | 8 | namespace Epinova.ElasticSearch.Core.EPiServer 9 | { 10 | [ServiceConfiguration(typeof(ISearchLanguage), Lifecycle = ServiceInstanceScope.Hybrid)] 11 | public class EpiserverSearchLanguage : ISearchLanguage 12 | { 13 | private List _enabledLanguages; 14 | 15 | public EpiserverSearchLanguage(ILanguageBranchRepository languageBranchRepository) 16 | { 17 | _enabledLanguages = languageBranchRepository.ListEnabled().Select(l => l.Culture).ToList(); 18 | } 19 | 20 | public CultureInfo SearchLanguage 21 | { 22 | get 23 | { 24 | if(CultureInfo.CurrentCulture.Equals(CultureInfo.InvariantCulture)) 25 | return CultureInfo.InvariantCulture; 26 | 27 | if(!_enabledLanguages.Any()) 28 | return CultureInfo.InvariantCulture; 29 | 30 | return GetEnabledCulture(CultureInfo.CurrentCulture) ?? GetEnabledCulture(CultureInfo.CurrentCulture.Parent) ?? GetEnabledCulture(CultureInfo.CurrentCulture.Parent.Parent) ?? CultureInfo.InvariantCulture; 31 | } 32 | } 33 | 34 | private CultureInfo GetEnabledCulture(CultureInfo cultureInfo) 35 | { 36 | return _enabledLanguages.SingleOrDefault(l => l.Equals(cultureInfo)); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Extensions/AdminStringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.EPiServer.Extensions 2 | { 3 | public static class AdminStringExtensions 4 | { 5 | public static string FixInput(this string input) 6 | { 7 | return input.Replace("\"", "\'\'"); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Extensions/CustomResultExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Epinova.ElasticSearch.Core.Contracts; 3 | using Epinova.ElasticSearch.Core.Models; 4 | using EPiServer.ServiceLocation; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Extensions 7 | { 8 | public static class CustomResultExtensions 9 | { 10 | private static readonly ITrackingRepository _trackingRepository = ServiceLocator.Current.GetInstance(); 11 | 12 | public static async Task> GetCustomResultsAsync(this IElasticSearchService service) 13 | { 14 | CustomSearchResult results = await service.GetResultsCustomAsync(); 15 | HandleTracking(service, results.TotalHits); 16 | return results; 17 | } 18 | 19 | public static CustomSearchResult GetCustomResults(this IElasticSearchService service) 20 | { 21 | CustomSearchResult results = service.GetResultsCustom(); 22 | HandleTracking(service, results.TotalHits); 23 | return results; 24 | } 25 | 26 | private static void HandleTracking(this IElasticSearchService service, int totalHits) 27 | { 28 | if(service.TrackSearch && !string.IsNullOrWhiteSpace(service.IndexName)) 29 | _trackingRepository.AddSearch(service, noHits: totalHits == 0); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Extensions/IndexingExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using Epinova.ElasticSearch.Core.Conventions; 4 | using EPiServer.Core; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Extensions 7 | { 8 | public static class IndexingExtensions 9 | { 10 | /// 11 | /// Register a property as searchable. 12 | /// 13 | /// Use this if you can't alter the source code of the object to index, 14 | /// thus preventing you from using [Searchable] 15 | /// 16 | /// 17 | public static Indexing IncludeProperty(this CustomPropertyConvention instance, Expression> fieldSelector) 18 | where T : IContent 19 | { 20 | string fieldName = ElasticSearchService.GetFieldInfo(fieldSelector).Item1; 21 | Type type = typeof(T); 22 | 23 | if(!Indexing.Instance.SearchableProperties.ContainsKey(type)) 24 | { 25 | Indexing.Instance.SearchableProperties.TryAdd(type, new[] { fieldName }); 26 | } 27 | else 28 | { 29 | if(Indexing.Instance.SearchableProperties.TryGetValue(type, out string[] current)) 30 | { 31 | var merged = new string[current.Length + 1]; 32 | current.CopyTo(merged, 1); 33 | merged[0] = fieldName; 34 | Indexing.Instance.SearchableProperties[type] = merged; 35 | } 36 | } 37 | 38 | return Indexing.Instance; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Extensions/LocalizationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web; 3 | using System.Web.Mvc; 4 | using EPiServer.Framework.Localization; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Extensions 7 | { 8 | public static class LocalizationExtensions 9 | { 10 | public static IHtmlString TranslateWithPathRaw(this HtmlHelper instance, string key, string localizationPath) => instance.Raw(LocalizationService.Current.GetString(String.Concat(localizationPath, key))); 11 | 12 | public static string TranslateWithPath(this HtmlHelper instance, string key, string localizationPath) => TranslateWithPath(key, localizationPath); 13 | 14 | internal static string TranslateWithPath(string key, string localizationPath) => LocalizationService.Current.GetString(String.Concat(localizationPath, key)); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Extensions/XhtmlStringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Security.Principal; 4 | using EPiServer.Core; 5 | using EPiServer.Core.Html.StringParsing; 6 | 7 | namespace Epinova.ElasticSearch.Core.EPiServer.Extensions 8 | { 9 | internal static class XhtmlStringExtensions 10 | { 11 | public static IEnumerable GetFragments(this XhtmlString instance, IPrincipal principal = null) 12 | { 13 | return principal == null 14 | ? instance.Fragments.OfType() 15 | : instance.Fragments.GetFilteredFragments(principal).OfType(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Initialization/IndexingInitializer.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.EPiServer.Events; 2 | using EPiServer.Core; 3 | using EPiServer.DataAbstraction; 4 | using EPiServer.Framework; 5 | using EPiServer.Framework.Initialization; 6 | 7 | namespace Epinova.ElasticSearch.Core.EPiServer.Initialization 8 | { 9 | [InitializableModule] 10 | [ModuleDependency(typeof(IndexInitializer))] 11 | public class IndexingInitializer : IInitializableModule 12 | { 13 | public void Initialize(InitializationEngine context) 14 | { 15 | IContentEvents events = context.Locate.Advanced.GetInstance(); 16 | 17 | events.PublishedContent += IndexingEvents.UpdateIndex; 18 | events.DeletingContent += IndexingEvents.DeleteFromIndex; 19 | events.MovedContent += IndexingEvents.UpdateIndex; 20 | events.SavedContent += IndexingEvents.UpdateIndex; 21 | 22 | IContentSecurityRepository contentSecurityRepository = context.Locate.Advanced.GetInstance(); 23 | contentSecurityRepository.ContentSecuritySaved += IndexingEvents.UpdateIndex; 24 | } 25 | 26 | public void Uninitialize(InitializationEngine context) 27 | { 28 | // Not applicable 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Initialization/LocalizationInitialization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Reflection; 5 | using EPiServer.Framework; 6 | using EPiServer.Framework.Initialization; 7 | using EPiServer.Framework.Localization; 8 | using EPiServer.Framework.Localization.XmlResources; 9 | 10 | namespace Epinova.ElasticSearch.Core.EPiServer.Initialization 11 | { 12 | [ModuleDependency(typeof(FrameworkInitialization))] 13 | public class LocalizationInitialization : IInitializableModule 14 | { 15 | public void Initialize(InitializationEngine context) 16 | { 17 | if(context.Locate.Advanced.GetInstance() is ProviderBasedLocalizationService localizationService) 18 | { 19 | var ass = Assembly.GetAssembly(typeof(LocalizationInitialization)); 20 | 21 | string[] xmlResources = 22 | ass.GetManifestResourceNames() 23 | .Where(r => r.EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase)) 24 | .ToArray(); 25 | 26 | foreach(string name in xmlResources) 27 | { 28 | Stream stream = ass.GetManifestResourceStream(name); 29 | var provider = new XmlLocalizationProvider(); 30 | provider.Initialize(name, null); 31 | provider.Load(stream); 32 | localizationService.AddProvider(provider); 33 | } 34 | } 35 | } 36 | 37 | public void Uninitialize(InitializationEngine context) 38 | { 39 | // Not applicable 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Initialization/SearchLanguageInitializer.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Contracts; 2 | using EPiServer.Framework; 3 | using EPiServer.Framework.Initialization; 4 | using EPiServer.ServiceLocation; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Initialization 7 | { 8 | [InitializableModule] 9 | [ModuleDependency(typeof(FrameworkInitialization))] 10 | public class DependencyResolverInitialization : IConfigurableModule 11 | { 12 | public void ConfigureContainer(ServiceConfigurationContext context) 13 | { 14 | context.ConfigurationComplete += (sender, args) => 15 | { 16 | context.Services.RemoveAll(); 17 | context.Services.Add(ServiceInstanceScope.Singleton); 18 | }; 19 | } 20 | 21 | public void Initialize(InitializationEngine context) 22 | { 23 | } 24 | 25 | public void Uninitialize(InitializationEngine context) 26 | { 27 | } 28 | 29 | 30 | public void Preload(string[] parameters) 31 | { 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Initialization/ViewEngineInitializer.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Mvc; 3 | using System.Web.WebPages; 4 | using EPiServer.Framework; 5 | using EPiServer.Framework.Initialization; 6 | using EPiServer.Initialization.Internal; 7 | using RazorGenerator.Mvc; 8 | 9 | namespace Epinova.ElasticSearch.Core.EPiServer.Initialization 10 | { 11 | [InitializableModule] 12 | [ModuleDependency(typeof(PlugInInitialization))] 13 | public class ViewEngineInitializer : IInitializableModule 14 | { 15 | public void Initialize(InitializationEngine context) 16 | { 17 | var engine = new PrecompiledMvcEngine(typeof(ViewEngineInitializer).Assembly) 18 | { 19 | UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal 20 | }; 21 | 22 | ViewEngines.Engines.Insert(0, engine); 23 | 24 | // StartPage lookups are done by WebPages. 25 | VirtualPathFactoryManager.RegisterVirtualPathFactory(engine); 26 | } 27 | 28 | public void Uninitialize(InitializationEngine context) 29 | { 30 | // Not applicable 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Install.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | #save the project file first - this commits the changes made by nuget before this script runs. 4 | $project.Save() 5 | 6 | 7 | $xml = [XML] (gc $project.FullName) 8 | 9 | $nsmgr = New-Object System.Xml.XmlNamespaceManager -ArgumentList $xml.NameTable 10 | $nsmgr.AddNamespace('a',$xml.Project.GetAttribute("xmlns")) 11 | 12 | $node = $xml.SelectSingleNode("//a:ProjectTypeGuids", $nsmgr) 13 | $isWebProject = $false 14 | if($node -ne $null) { 15 | $guid = [String]$node.InnerText.ToUpper(); 16 | $isWebProject = $guid.Contains("{349C5851-65DF-11DA-9384-00065B846F21}") 17 | } 18 | 19 | if($isWebProject -eq $false) { 20 | Write-Host $project.FullName " is not a web project" 21 | 22 | $modules = $project.ProjectItems.Item("Modules"); 23 | 24 | Write-Host "Deleting _protected folder" 25 | $modules.ProjectItems.Item("_protected").Delete() 26 | 27 | if($modules.ProjectItems.Count -eq 0) { 28 | Write-Host "Deleting modules folder because it is empty" 29 | $modules.Delete() 30 | } 31 | 32 | $project.Save() 33 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/AutoSuggestFile.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | using EPiServer.DataAbstraction; 3 | using EPiServer.DataAnnotations; 4 | using EPiServer.Framework.DataAnnotations; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 7 | { 8 | [ContentType(GUID = "0859284D-8570-4142-A1AC-3777AEA7B79C")] 9 | [AdministrationSettings(Visible = false, CodeOnly = true, GroupName = "mediatypes", 10 | PropertyDefinitionFields = PropertyDefinitionFields.All ^ PropertyDefinitionFields.DisplayEditUI ^ PropertyDefinitionFields.LanguageSpecific ^ PropertyDefinitionFields.Searchable)] 11 | [MediaDescriptor(ExtensionString = "autosuggest")] 12 | [AvailableContentTypes(Availability = Availability.None)] 13 | internal class AutoSuggestFile : MediaData 14 | { 15 | public virtual string LanguageId { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/AutoSuggestFileFolder.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | using EPiServer.DataAbstraction; 3 | using EPiServer.DataAnnotations; 4 | 5 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 6 | { 7 | [ContentType(GUID = "50A5C46D-51DA-4643-88F7-8959CD37C9AC")] 8 | [AdministrationSettings(Visible = false, CodeOnly = true, GroupName = "mediatypes", 9 | PropertyDefinitionFields = PropertyDefinitionFields.All ^ PropertyDefinitionFields.DisplayEditUI ^ PropertyDefinitionFields.LanguageSpecific ^ PropertyDefinitionFields.Searchable)] 10 | [AvailableContentTypes(Include = new[] { typeof(AutoSuggestFile) })] 11 | internal class AutoSuggestFileFolder : ContentFolder 12 | { 13 | } 14 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/BestBetsFile.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | using EPiServer.DataAbstraction; 3 | using EPiServer.DataAnnotations; 4 | using EPiServer.Framework.DataAnnotations; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 7 | { 8 | [ContentType(GUID = "CB1F03BD-8D75-4E14-8CB9-33274627F429")] 9 | [AdministrationSettings(Visible = false, CodeOnly = true, GroupName = "mediatypes", 10 | PropertyDefinitionFields = PropertyDefinitionFields.All ^ PropertyDefinitionFields.DisplayEditUI ^ PropertyDefinitionFields.LanguageSpecific ^ PropertyDefinitionFields.Searchable)] 11 | [MediaDescriptor(ExtensionString = Extension)] 12 | [AvailableContentTypes(Availability = Availability.None)] 13 | internal class BestBetsFile : MediaData 14 | { 15 | internal const string Extension = "bestbets"; 16 | public virtual string LanguageId { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/BestBetsFileFolder.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | using EPiServer.DataAbstraction; 3 | using EPiServer.DataAnnotations; 4 | 5 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 6 | { 7 | [ContentType(GUID = "BB777539-A342-4A4D-A190-18EA42636215")] 8 | [AdministrationSettings(Visible = false, CodeOnly = true, GroupName = "mediatypes", 9 | PropertyDefinitionFields = PropertyDefinitionFields.All ^ PropertyDefinitionFields.DisplayEditUI ^ PropertyDefinitionFields.LanguageSpecific ^ PropertyDefinitionFields.Searchable)] 10 | [AvailableContentTypes(Include = new[] { typeof(BestBetsFile) })] 11 | internal class BestBetsFileFolder : ContentFolder 12 | { 13 | internal const string ContentName = "Elasticsearch Best Bets"; 14 | } 15 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/BoostItem.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 2 | { 3 | public class BoostItem 4 | { 5 | public string TypeName { get; set; } 6 | public string GroupName { get; set; } 7 | public string DisplayName { get; set; } 8 | public int Weight { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/BoostingData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EPiServer.Core; 3 | using EPiServer.DataAbstraction; 4 | using EPiServer.DataAnnotations; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 7 | { 8 | [ContentType(GUID = "AF034397-F180-4CE0-B057-286152AA52B2")] 9 | [AdministrationSettings(Visible = false, CodeOnly = true, 10 | PropertyDefinitionFields = 11 | PropertyDefinitionFields.All ^ PropertyDefinitionFields.DisplayEditUI ^ 12 | PropertyDefinitionFields.LanguageSpecific ^ PropertyDefinitionFields.Searchable)] 13 | [AvailableContentTypes(Availability = Availability.None)] 14 | internal class BoostingData : IContent 15 | { 16 | public BoostingData() 17 | { 18 | Property = new PropertyDataCollection(); 19 | } 20 | 21 | public virtual string FieldName { get; set; } 22 | public virtual int Weight { get; set; } 23 | 24 | // IContent 25 | public string Name { get; set; } 26 | public ContentReference ContentLink { get; set; } 27 | public ContentReference ParentLink { get; set; } 28 | public Guid ContentGuid { get; set; } 29 | public int ContentTypeID { get; set; } 30 | public bool IsDeleted { get; set; } 31 | public PropertyDataCollection Property { get; } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/BoostingFolder.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | using EPiServer.DataAbstraction; 3 | using EPiServer.DataAnnotations; 4 | 5 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 6 | { 7 | [ContentType(GUID = "A93DA279-08AD-4D47-952D-70FD566B66C5")] 8 | [AdministrationSettings(Visible = false, CodeOnly = true, 9 | PropertyDefinitionFields = PropertyDefinitionFields.All ^ PropertyDefinitionFields.DisplayEditUI ^ PropertyDefinitionFields.LanguageSpecific ^ PropertyDefinitionFields.Searchable)] 10 | [AvailableContentTypes(Include = new[] { typeof(BoostingData) })] 11 | public class BoostingFolder : ContentFolder 12 | { 13 | } 14 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/BoostingInputModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 4 | { 5 | public class BoostingInputModel 6 | { 7 | public BoostingInputModel() 8 | { 9 | Boosting = new Dictionary(); 10 | } 11 | 12 | public string TypeName { get; set; } 13 | 14 | public Dictionary Boosting { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/BoostingViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 4 | { 5 | public class BoostingViewModel 6 | { 7 | public BoostingViewModel() 8 | { 9 | BoostingByType = new Dictionary>(); 10 | } 11 | 12 | public Dictionary> BoostingByType { get; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/Synonym.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 2 | { 3 | public class Synonym 4 | { 5 | public string From { get; set; } 6 | public string To { get; set; } 7 | public bool TwoWay { get; set; } 8 | public bool MultiWord { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/SynonymBackupFile.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | using EPiServer.DataAbstraction; 3 | using EPiServer.DataAnnotations; 4 | using EPiServer.Framework.DataAnnotations; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 7 | { 8 | [ContentType(GUID = "151C521D-5A01-4CC0-8EEC-8808F92D8F85")] 9 | [AdministrationSettings(Visible = false, CodeOnly = true, GroupName = "mediatypes", 10 | PropertyDefinitionFields = PropertyDefinitionFields.All ^ PropertyDefinitionFields.DisplayEditUI ^ PropertyDefinitionFields.LanguageSpecific ^ PropertyDefinitionFields.Searchable)] 11 | [MediaDescriptor(ExtensionString = "synonyms")] 12 | [AvailableContentTypes(Availability = Availability.None)] 13 | internal class SynonymBackupFile : MediaData 14 | { 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/SynonymBackupFileFolder.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | using EPiServer.DataAbstraction; 3 | using EPiServer.DataAnnotations; 4 | 5 | namespace Epinova.ElasticSearch.Core.EPiServer.Models 6 | { 7 | [ContentType(GUID = "0000158F-E62F-4F4A-875B-A59DF096E956")] 8 | [AdministrationSettings(Visible = false, CodeOnly = true, GroupName = "mediatypes", 9 | PropertyDefinitionFields = PropertyDefinitionFields.All ^ PropertyDefinitionFields.DisplayEditUI ^ PropertyDefinitionFields.LanguageSpecific ^ PropertyDefinitionFields.Searchable)] 10 | [AvailableContentTypes(Include = new[] { typeof(SynonymBackupFile) })] 11 | internal class SynonymBackupFileFolder : ContentFolder 12 | { 13 | } 14 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/Abstractions/LanguageAwareViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions 4 | { 5 | public abstract class LanguageAwareViewModelBase 6 | { 7 | protected LanguageAwareViewModelBase(string currentLanguage) 8 | { 9 | CurrentLanguage = currentLanguage ?? String.Empty; 10 | } 11 | 12 | public string CurrentLanguage { get; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/Abstractions/LanguageViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions 5 | { 6 | public abstract class LanguageViewModelBase 7 | { 8 | public string LanguageId { get; set; } 9 | 10 | public string LanguageName { get; set; } 11 | 12 | public string IndexName { get; set; } 13 | 14 | public Dictionary Indices { get; internal set; } = new Dictionary(); 15 | 16 | public CultureInfo Language => new CultureInfo(LanguageId); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/AdminViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.Models.Admin; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 5 | { 6 | public class AdminViewModel 7 | { 8 | public AdminViewModel(HealthInformation clusterHealth, IEnumerable allIndexes, Node[] nodeInfo) 9 | { 10 | NodeInfo = nodeInfo; 11 | ClusterHealth = clusterHealth; 12 | AllIndexes = allIndexes; 13 | } 14 | 15 | public Node[] NodeInfo { get; } 16 | public HealthInformation ClusterHealth { get; } 17 | public IEnumerable AllIndexes { get; } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/AutoSuggestViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 5 | { 6 | public class AutoSuggestViewModel : LanguageAwareViewModelBase 7 | { 8 | public AutoSuggestViewModel(string currentLanguage) : base(currentLanguage) 9 | { 10 | WordsByLanguage = new List(); 11 | } 12 | 13 | public List WordsByLanguage { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/BestBetsByLanguage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.Conventions; 3 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 4 | 5 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 6 | { 7 | public class BestBetsByLanguage : LanguageViewModelBase 8 | { 9 | public IEnumerable BestBets { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/ConsoleViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 4 | { 5 | public class ConsoleViewModel 6 | { 7 | public string SelectedIndex { get; } 8 | public List Indices { get; } 9 | public string Query { get; } 10 | public string Result { get; set; } 11 | 12 | 13 | public ConsoleViewModel(string selectedIndex, List indices, string query) 14 | { 15 | SelectedIndex = selectedIndex; 16 | Indices = indices; 17 | Query = query; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/InspectLanguage.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 2 | 3 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 4 | { 5 | public class InspectLanguage : LanguageViewModelBase 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/InspectViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 4 | using Epinova.ElasticSearch.Core.Models; 5 | using Epinova.ElasticSearch.Core.Models.Admin; 6 | 7 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 8 | { 9 | public class InspectViewModel 10 | { 11 | public string SearchText { get; set; } 12 | 13 | public bool Analyzed { get; set; } 14 | 15 | public Dictionary> TypeCounts { get; set; } 16 | public string SelectedType { get; set; } 17 | 18 | public int SelectedNumberOfItems { get; set; } 19 | public List NumberOfItems { get; set; } 20 | 21 | public List SearchHits { get; set; } 22 | public List> Indices { get; set; } 23 | public string SelectedIndex { get; set; } 24 | public string SelectedIndexName { get; set; } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/LanguageAutoSuggestWords.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 5 | { 6 | public class LanguageAutoSuggestWords : LanguageViewModelBase 7 | { 8 | public List Words { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/LanguageSynonyms.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 5 | { 6 | public class LanguageSynonyms : LanguageViewModelBase 7 | { 8 | public string Analyzer { get; set; } 9 | 10 | public List Synonyms { get; set; } 11 | 12 | public bool HasSynonymsFile { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/MappingViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 4 | using Epinova.ElasticSearch.Core.Models.Admin; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 7 | { 8 | public class MappingViewModel : LanguageAwareViewModelBase 9 | { 10 | public MappingViewModel() : this(String.Empty) 11 | { 12 | } 13 | 14 | public MappingViewModel(string currentLanguage) : base(currentLanguage) 15 | { 16 | } 17 | 18 | public string SelectedIndex { get; set; } 19 | public List Indices { get; set; } 20 | public string Mappings { get; set; } 21 | } 22 | 23 | 24 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/SettingsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 4 | { 5 | public class SettingsViewModel 6 | { 7 | public string SelectedIndex { get; } 8 | public List Indices { get; } 9 | public string Result { get; set; } 10 | 11 | public SettingsViewModel(string selectedIndex, List indices) 12 | { 13 | SelectedIndex = selectedIndex; 14 | Indices = indices; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/SynonymsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 5 | { 6 | public class SynonymsViewModel : LanguageAwareViewModelBase 7 | { 8 | public SynonymsViewModel(string currentLanguage) : base(currentLanguage) 9 | { 10 | SynonymsByLanguage = new List(); 11 | } 12 | 13 | public List SynonymsByLanguage { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/TrackingByLanguage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 5 | { 6 | public class TrackingByLanguage : LanguageViewModelBase 7 | { 8 | public Dictionary Searches { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/TrackingLanguage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 5 | { 6 | public class TrackingLanguage : LanguageViewModelBase 7 | { 8 | public Dictionary Searches { get; internal set; } = new Dictionary(); 9 | 10 | public Dictionary SearchesWithoutHits { get; internal set; } = new Dictionary(); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Models/ViewModels/TrackingViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 3 | 4 | namespace Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels 5 | { 6 | public class TrackingViewModel : LanguageAwareViewModelBase 7 | { 8 | public TrackingViewModel(string currentLanguage) : base(currentLanguage) 9 | { 10 | } 11 | 12 | public List Languages { get; } = new List(); 13 | public string SelectedIndex { get; set; } 14 | 15 | 16 | public void AddLanguage(string name, string id, Dictionary indices, Dictionary searches, Dictionary searchesWithoutHits) 17 | { 18 | Languages.Add(new TrackingLanguage 19 | { 20 | LanguageId = id, 21 | LanguageName = name, 22 | Indices = indices, 23 | Searches = searches, 24 | SearchesWithoutHits = searchesWithoutHits 25 | }); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | [assembly: ComVisible(false)] 5 | [assembly: Guid("f5a0a67a-4a0f-4a91-b54d-b3fcb1410765")] 6 | [assembly: InternalsVisibleTo("Epinova.ElasticSearch.Core.EPiServer.Commerce")] 7 | [assembly: InternalsVisibleTo("Core.EPiServer.Tests")] 8 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Providers/BlockSearchProvider.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | using EPiServer.DataAbstraction; 3 | using EPiServer.Shell.Search; 4 | 5 | namespace Epinova.ElasticSearch.Core.EPiServer.Providers 6 | { 7 | [SearchProvider] 8 | public class BlockSearchProvider : SearchProviderBase 9 | { 10 | public BlockSearchProvider() : base("block") 11 | { 12 | IconClass = ProviderConstants.BlockIconCssClass; 13 | AreaName = ProviderConstants.BlockArea; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Providers/FileSearchProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using EPiServer.Core; 3 | using EPiServer.DataAbstraction; 4 | using EPiServer.Shell.Search; 5 | 6 | namespace Epinova.ElasticSearch.Core.EPiServer.Providers 7 | { 8 | [SearchProvider] 9 | public class FileSearchProvider : SearchProviderBase 10 | { 11 | public FileSearchProvider() : base("file", CultureInfo.InvariantCulture) 12 | { 13 | IconClass = ProviderConstants.FileIconCssClass; 14 | AreaName = ProviderConstants.FileArea; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Providers/PageSearchProvider.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | using EPiServer.DataAbstraction; 3 | using EPiServer.Shell.Search; 4 | 5 | namespace Epinova.ElasticSearch.Core.EPiServer.Providers 6 | { 7 | [SearchProvider] 8 | public class PageSearchProvider : SearchProviderBase 9 | { 10 | public PageSearchProvider() : base("page") 11 | { 12 | IconClass = ProviderConstants.PageIconCssClass; 13 | AreaName = ProviderConstants.PageArea; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/RoleNames.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.EPiServer 2 | { 3 | internal static class RoleNames 4 | { 5 | internal const string ElasticsearchAdmins = nameof(ElasticsearchAdmins); 6 | internal const string ElasticsearchEditors = nameof(ElasticsearchEditors); 7 | } 8 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Services/ContentIndexService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using Epinova.ElasticSearch.Core.EPiServer.Contracts; 6 | using EPiServer; 7 | using EPiServer.Core; 8 | using EPiServer.DataAbstraction; 9 | using EPiServer.ServiceLocation; 10 | 11 | namespace Epinova.ElasticSearch.Core.EPiServer.Services 12 | { 13 | [ServiceConfiguration(ServiceType = typeof(IContentIndexService), Lifecycle = ServiceInstanceScope.Transient)] 14 | public class ContentIndexService : IContentIndexService 15 | { 16 | private readonly IContentLoader _contentLoader; 17 | 18 | public ContentIndexService(IContentLoader contentLoader) 19 | { 20 | _contentLoader = contentLoader; 21 | } 22 | 23 | public IEnumerable ListContent(List contentReferences, List languages) 24 | { 25 | return languages.Any() 26 | ? languages.SelectMany(l => _contentLoader.GetItems(contentReferences, l.Culture)) 27 | : _contentLoader.GetItems(contentReferences, CultureInfo.InvariantCulture); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/Views/ElasticSearchAdmin/Settings/Index.cshtml: -------------------------------------------------------------------------------- 1 | @* DisableLinePragmas: true *@ 2 | @using System.Web.Mvc.Html 3 | @using Epinova.ElasticSearch.Core.EPiServer.Extensions 4 | @using EPiServer.Shell.Web.Mvc.Html 5 | @model Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.SettingsViewModel 6 | 7 | @{ 8 | Layout = "~/Views/ElasticSearchAdmin/_ElasticSearch.cshtml"; 9 | string localizationPath = "/epinovaelasticsearch/console/"; 10 | } 11 | 12 |
13 | @using (Html.BeginForm("Index", "ElasticSettings")) 14 | { 15 |

@Html.TranslateWithPathRaw("settings", localizationPath)

16 |

17 | 26 | 27 |

28 | } 29 | 30 | @if (Model.Result != null) 31 | { 32 |
@Model.Result
33 | } 34 |
-------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/modules/_protected/ElasticSearch/ClientResources/Scripts/ToolbarProvider.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'dojo/_base/declare', 3 | 'dijit/form/Button', 4 | 'epi-cms/component/command/_GlobalToolbarCommandProvider', 5 | 'epi/i18n!epi/nls/epinovaelasticsearch.widget', 6 | 'epinova-elasticsearch/UpdateIndexCommand' 7 | ], 8 | function ( 9 | declare, 10 | Button, 11 | _GlobalToolbarCommandProvider, 12 | translator, 13 | UpdateIndexCommand 14 | ) { 15 | return declare([_GlobalToolbarCommandProvider], { 16 | constructor: function () { 17 | this.inherited(arguments); 18 | 19 | this.addToCenter(new UpdateIndexCommand({ label: translator.button.label }), { showLabel: true, widget: Button }); 20 | } 21 | }); 22 | }); -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/modules/_protected/ElasticSearch/ClientResources/Scripts/UpdateIndexCommand.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'dojo/_base/declare', 3 | 'epi/shell/command/_Command', 4 | 'epi/i18n!epi/nls/epinovaelasticsearch.widget' 5 | ], function ( 6 | declare, 7 | _Command, 8 | translator 9 | ) { 10 | return declare([_Command], { 11 | name: 'updateIndex', 12 | label: translator.button.label, 13 | tooltip: translator.button.tooltip, 14 | iconClass: 'epi-iconSortAscending', 15 | canExecute: true, 16 | 17 | _execute: function () { 18 | dojo.rawXhrPost({ 19 | url: '/ElasticSearchAdmin/ElasticIndexer/UpdateItem', 20 | handleAs: 'json', 21 | headers: { "Content-Type": 'application/json' }, 22 | timeout: 10000, 23 | postData: dojo.toJson({ "id": this.model.contentLink }), 24 | load: function (data) { 25 | if (!!console && !!console.info) { 26 | console.info(data.status); 27 | } 28 | }, 29 | error: function (error) { 30 | alert('An error occured, unable to update index. Status: ' + error); 31 | } 32 | }); 33 | } 34 | }); 35 | }); -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/modules/_protected/ElasticSearch/ClientResources/Scripts/UpdateTreeStructureCommand.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'dojo/topic', 3 | 'dojo/_base/declare', 4 | 'epi/dependency', 5 | 'epi/shell/command/_Command', 6 | 'epi/i18n!epi/nls/epinovaelasticsearch.widget' 7 | ], function ( 8 | topic, 9 | declare, 10 | dependency, 11 | _Command, 12 | translator 13 | ) { 14 | return declare([_Command], { 15 | name: 'updateStructure', 16 | label: translator.button.label, 17 | tooltip: translator.button.tooltip, 18 | iconClass: 'epi-iconSortAscending', 19 | canExecute: true, 20 | 21 | _execute: function () { 22 | dojo.rawXhrPost({ 23 | url: '/ElasticSearchAdmin/ElasticIndexer/UpdateItem', 24 | handleAs: 'json', 25 | headers: { "Content-Type": 'application/json' }, 26 | timeout: 600000, 27 | postData: dojo.toJson({ "id": this.model.contentLink, "recursive": true }), 28 | load: function (data) { 29 | if (!!console && !!console.info) { 30 | console.info(data.status); 31 | } 32 | }, 33 | error: function (error) { 34 | alert('An error occured, unable to update index. Status: ' + error); 35 | } 36 | }); 37 | } 38 | }); 39 | }); -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/modules/_protected/ElasticSearch/ClientResources/Scripts/init.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'dojo', 3 | 'epi/dependency', 4 | 'epi-cms/plugin-area/navigation-tree', 5 | 'epinova-elasticsearch/ToolbarProvider', 6 | 'epinova-elasticsearch/UpdateTreeStructureCommand' 7 | ], 8 | function ( 9 | dojo, 10 | dependency, 11 | navigationTreePluginArea, 12 | ToolbarProvider, 13 | UpdateTreeStructureCommand 14 | ) { 15 | return dojo.declare([], { 16 | initialize: function () { 17 | var commandregistry = dependency.resolve('epi.globalcommandregistry'); 18 | commandregistry.registerProvider('epi.cms.contentdetailsmenu', new ToolbarProvider()); 19 | navigationTreePluginArea.add(UpdateTreeStructureCommand); 20 | } 21 | }); 22 | }); -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/modules/_protected/ElasticSearch/ElasticSearch.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/src/Epinova.ElasticSearch.Core.EPiServer/modules/_protected/ElasticSearch/ElasticSearch.zip -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/modules/_protected/ElasticSearch/module.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core.EPiServer/modules/_protected/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Admin/Health.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Epinova.ElasticSearch.Core.Contracts; 4 | using Epinova.ElasticSearch.Core.Models.Admin; 5 | using Epinova.ElasticSearch.Core.Settings; 6 | using Newtonsoft.Json; 7 | 8 | namespace Epinova.ElasticSearch.Core.Admin 9 | { 10 | internal class Health 11 | { 12 | private readonly IElasticSearchSettings _settings; 13 | private readonly IHttpClientHelper _httpClientHelper; 14 | 15 | public Health( 16 | IElasticSearchSettings settings, 17 | IHttpClientHelper httpClientHelper) 18 | { 19 | _settings = settings; 20 | _httpClientHelper = httpClientHelper; 21 | } 22 | 23 | public virtual HealthInformation GetClusterHealth() 24 | { 25 | var uri = $"{_settings.Host}/_cat/health?format=json"; 26 | var json = _httpClientHelper.GetJson(new Uri(uri)); 27 | 28 | return GetClusterHealth(json); 29 | } 30 | 31 | internal static HealthInformation GetClusterHealth(string json) 32 | => JsonConvert.DeserializeObject(json).FirstOrDefault(); 33 | 34 | public virtual Node[] GetNodeInfo() 35 | { 36 | var uri = $"{_settings.Host}/_cat/nodes?format=json&h=m,v,http,d,rc,rm,u,n"; 37 | var json = _httpClientHelper.GetJson(new Uri(uri)); 38 | 39 | return GetNodeInfo(json); 40 | } 41 | 42 | internal static Node[] GetNodeInfo(string json) 43 | => JsonConvert.DeserializeObject(json); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Attributes/BoostAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Epinova.ElasticSearch.Core.Attributes 4 | { 5 | /// 6 | /// Indicates that the property should be scored higher. Weight defaults to 1 if not specified. 7 | /// 8 | [AttributeUsage(AttributeTargets.Property)] 9 | public class BoostAttribute : Attribute 10 | { 11 | public BoostAttribute() 12 | : this(1) 13 | { 14 | } 15 | 16 | /// 17 | /// Initializes a new instance of the class 18 | /// and sets the value of 19 | /// 20 | /// The weight to set 21 | public BoostAttribute(int weight) 22 | { 23 | Weight = weight; 24 | } 25 | 26 | /// 27 | /// 28 | /// 29 | public int Weight { get; set; } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Attributes/DidYouMeanSourceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Epinova.ElasticSearch.Core.Attributes 4 | { 5 | /// 6 | /// Indicates that this property should be included as a source to Did You Mean queries. 7 | /// Only works on string-types. 8 | /// 9 | [AttributeUsage(AttributeTargets.Property)] 10 | public class DidYouMeanSourceAttribute : Attribute 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Attributes/ExcludeFromSearchAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Epinova.ElasticSearch.Core.Attributes 4 | { 5 | /// 6 | /// Excludes type from Elastic Search 7 | /// 8 | [AttributeUsage(AttributeTargets.Class)] 9 | public class ExcludeFromSearchAttribute : Attribute 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Attributes/StemAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Epinova.ElasticSearch.Core.Attributes 4 | { 5 | /// 6 | /// Indicates that this property should be analyzed with a language-analyzer upon indexing, in essence enabling stemming. 7 | /// Only works on string-types. 8 | /// 9 | [AttributeUsage(AttributeTargets.Property)] 10 | public class StemAttribute : Attribute 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Contracts/IBestBetsRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using Epinova.ElasticSearch.Core.Conventions; 5 | 6 | namespace Epinova.ElasticSearch.Core.Contracts 7 | { 8 | public interface IBestBetsRepository 9 | { 10 | void AddBestBet(CultureInfo language, string phrase, long id, string index, Type type); 11 | void DeleteBestBet(CultureInfo language, string phrase, long id, string index, Type type); 12 | IEnumerable GetBestBets(CultureInfo language, string index); 13 | IEnumerable GetBestBetsForContent(CultureInfo language, int contentId, string index, bool isCommerceContent = false); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Contracts/IBoostingRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Epinova.ElasticSearch.Core.Contracts 5 | { 6 | public interface IBoostingRepository 7 | { 8 | Dictionary GetByType(Type type); 9 | void Save(string typeName, Dictionary boosting); 10 | void DeleteAll(); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Contracts/IElasticSearchService.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Contracts 2 | { 3 | /// 4 | /// The ElasticSearch service 5 | /// 6 | public interface IElasticSearchService : IElasticSearchService 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Contracts/IHttpClientHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Epinova.ElasticSearch.Core.Contracts 8 | { 9 | public interface IHttpClientHelper 10 | { 11 | bool Delete(Uri uri); 12 | Task DeleteAsync(Uri uri); 13 | string GetJson(Uri uri); 14 | string GetString(Uri uri); 15 | Task GetStringAsync(Uri uri); 16 | HttpStatusCode Head(Uri uri); 17 | Task HeadAsync(Uri uri); 18 | byte[] Post(Uri uri, byte[] data = null); 19 | byte[] Post(Uri uri, Stream data = null); 20 | Task PostAsync(Uri uri, byte[] data, CancellationToken cancellationToken); 21 | void Put(Uri uri, byte[] data = null); 22 | Task PutAsync(Uri uri, byte[] data = null); 23 | Task PutAsync(Uri uri, byte[] data, CancellationToken cancellationToken); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Contracts/IInspectorRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.Models; 3 | 4 | namespace Epinova.ElasticSearch.Core.Contracts 5 | { 6 | public interface IInspectorRepository 7 | { 8 | List Search(string searchText, bool analyzed, string indexName, int size, string type = null); 9 | Dictionary> GetTypes(string searchText, string indexName); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Contracts/IProperty.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Contracts 2 | { 3 | public interface IProperty 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Contracts/ISearchLanguage.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace Epinova.ElasticSearch.Core.Contracts 4 | { 5 | public interface ISearchLanguage 6 | { 7 | CultureInfo SearchLanguage { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Contracts/IServerInfoService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.Models.Admin; 3 | 4 | namespace Epinova.ElasticSearch.Core.Contracts 5 | { 6 | public interface IServerInfoService 7 | { 8 | ServerInfo GetInfo(); 9 | IEnumerable ListPlugins(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Contracts/ITrackingRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.Models; 3 | 4 | namespace Epinova.ElasticSearch.Core.Contracts 5 | { 6 | public interface ITrackingRepository 7 | { 8 | void AddSearch(IElasticSearchService service, bool noHits); 9 | void Clear(string languageId, string index); 10 | IEnumerable GetSearches(string languageId, string index); 11 | IEnumerable GetSearchesWithoutHits(string languageId, string index); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/BestBet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Epinova.ElasticSearch.Core.Conventions 5 | { 6 | public sealed class BestBet 7 | { 8 | public BestBet(string phrase, long id) 9 | { 10 | Id = id; 11 | Phrase = phrase; 12 | } 13 | 14 | public long Id { get; } 15 | 16 | public string Provider => ""; 17 | public string Url => ""; 18 | 19 | public string Name { get; set; } 20 | 21 | public string Phrase { get; } 22 | 23 | internal string[] GetTerms() 24 | { 25 | return String.IsNullOrWhiteSpace(Phrase) 26 | ? Array.Empty() 27 | : Phrase.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) 28 | .Select(t => t.Trim()) 29 | .ToArray(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/CustomProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Epinova.ElasticSearch.Core.Conventions 4 | { 5 | /// 6 | /// Defines a custom property added at index-time 7 | /// 8 | public class CustomProperty 9 | { 10 | /// 11 | /// The property name 12 | /// 13 | public string Name { get; } 14 | 15 | /// 16 | /// The property type 17 | /// 18 | public Type Type { get; } 19 | 20 | /// 21 | /// The owner type 22 | /// 23 | public Type OwnerType { get; } 24 | 25 | /// 26 | /// The property getter method 27 | /// 28 | public Delegate Getter { get; } 29 | 30 | /// 31 | /// Initializes a new instance of the class 32 | /// 33 | public CustomProperty(string name, Delegate getter, Type ownerType) 34 | { 35 | Name = name; 36 | Type = getter.Method.ReturnType; 37 | OwnerType = ownerType; 38 | Getter = getter; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/Indexing.CustomProperties.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | 5 | namespace Epinova.ElasticSearch.Core.Conventions 6 | { 7 | public sealed partial class Indexing 8 | { 9 | internal ConcurrentDictionary SearchableProperties { get; } = new ConcurrentDictionary(); 10 | 11 | internal static List CustomProperties { get; } = new List(); 12 | internal static List ExcludedProperties { get; } = new List(); 13 | 14 | /// 15 | /// Add a convention for the specified type 16 | /// 17 | /// The type 18 | /// An instance of 19 | public CustomPropertyConvention ForType() 20 | => new CustomPropertyConvention(this); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/Indexing.Files.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Epinova.ElasticSearch.Core.Settings.Configuration; 4 | using EPiServer.Logging; 5 | 6 | namespace Epinova.ElasticSearch.Core.Conventions 7 | { 8 | public sealed partial class Indexing 9 | { 10 | internal static readonly List Extensions = new List(); 11 | 12 | internal static string[] IncludedFileExtensions => Extensions.ToArray(); 13 | 14 | /// 15 | /// Include the specified file-extension in the index 16 | /// 17 | /// The extension, e.g. pdf, doc, xls 18 | /// The instance 19 | public Indexing IncludeFileType(string extension) 20 | { 21 | var config = ElasticSearchSection.GetConfiguration(); 22 | if(!config.Files.Enabled) 23 | { 24 | Logger.Information($"Not adding '{extension}', file indexing is disabled"); 25 | return this; 26 | } 27 | 28 | if(!String.IsNullOrWhiteSpace(extension)) 29 | { 30 | Extensions.Add(extension.Trim(' ', '.').ToLower()); 31 | } 32 | 33 | return this; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/Indexing.Highlighting.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Epinova.ElasticSearch.Core.Conventions 4 | { 5 | public sealed partial class Indexing 6 | { 7 | internal static int HighlightFragmentSize { get; private set; } = 150; 8 | internal static string HighlightTag { get; private set; } = "mark"; 9 | 10 | /// 11 | /// Sets how many characters the highlighted excerpt should return 12 | /// Defaults to 150 13 | /// 14 | public void SetHighlightFragmentSize(int size) => 15 | HighlightFragmentSize = size; 16 | 17 | /// 18 | /// Sets the html-element to use on highlighted words 19 | /// Defaults to "mark" 20 | /// Set to null to disable 21 | /// 22 | public void SetHighlightTag(string tag) => 23 | HighlightTag = tag; 24 | 25 | internal static List Highlights { get; } = new List(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/Indexing.Roots.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using EPiServer.Logging; 3 | 4 | namespace Epinova.ElasticSearch.Core.Conventions 5 | { 6 | public sealed partial class Indexing 7 | { 8 | internal static readonly List Roots = new List(); 9 | 10 | internal static int[] ExcludedRoots => Roots.ToArray(); 11 | 12 | /// 13 | /// Excludes the specified root id and its children from the index. 14 | /// 15 | /// The instance 16 | public Indexing ExcludeRoot(int rootId) 17 | { 18 | if(!Roots.Contains(rootId)) 19 | { 20 | Logger.Information($"Excluding root: {rootId}"); 21 | Roots.Add(rootId); 22 | } 23 | 24 | return this; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/Indexing.Suggestions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Epinova.ElasticSearch.Core.Conventions 4 | { 5 | public sealed partial class Indexing 6 | { 7 | internal static readonly List Suggestions = new List(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/Indexing.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Logging; 2 | 3 | namespace Epinova.ElasticSearch.Core.Conventions 4 | { 5 | /// 6 | /// Contains methods for configuring custom conventions for the search. 7 | /// Should only be run once at application start. 8 | /// 9 | public sealed partial class Indexing 10 | { 11 | private static readonly ILogger Logger = LogManager.GetLogger(typeof(Indexing)); 12 | 13 | private Indexing() 14 | { 15 | } 16 | 17 | static Indexing() 18 | { 19 | SetupBestBets(); 20 | } 21 | 22 | /// 23 | /// The singleton instance property 24 | /// 25 | public static Indexing Instance { get; } = new Indexing(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/MessageHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | 3 | namespace Epinova.ElasticSearch.Core.Conventions 4 | { 5 | /// 6 | /// This implementation contains a single instance of . 7 | /// If you want several s, we recommend chaining them before including. 8 | /// 9 | public sealed class MessageHandler 10 | { 11 | private MessageHandler() 12 | { 13 | } 14 | 15 | public static readonly MessageHandler Instance = new MessageHandler(); 16 | 17 | internal HttpMessageHandler Handler; 18 | 19 | public void SetMessageHandler(HttpMessageHandler handler) 20 | { 21 | Handler = handler; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/MoreLikeThis.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using EPiServer.Core; 3 | 4 | namespace Epinova.ElasticSearch.Core.Conventions 5 | { 6 | /// 7 | /// Contains methods for configuring More Like This 8 | /// 9 | public sealed class MoreLikeThis 10 | { 11 | private MoreLikeThis() 12 | { 13 | AddComponentField(nameof(IContent.Name)); 14 | } 15 | 16 | /// 17 | /// Add field to be searched in the More Like This component/widget 18 | /// Default value is "Name" 19 | /// 20 | public void AddComponentField(string name) 21 | => ComponentFields.TryAdd(name, true); 22 | 23 | internal static ConcurrentDictionary ComponentFields { get; } 24 | = new ConcurrentDictionary(); 25 | 26 | /// 27 | /// The singleton instance property 28 | /// 29 | public static MoreLikeThis Instance { get; } = new MoreLikeThis(); 30 | } 31 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Conventions/Suggestion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Epinova.ElasticSearch.Core.Conventions 5 | { 6 | internal sealed class Suggestion 7 | { 8 | public Suggestion(Type type, bool includeAllFields = false) 9 | { 10 | Type = type; 11 | IncludeAllFields = includeAllFields; 12 | InputFields = new List(); 13 | } 14 | 15 | 16 | internal bool IncludeAllFields { get; set; } 17 | internal Type Type { get; } 18 | internal List InputFields { get; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/DefaultFields.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core 2 | { 3 | public static class DefaultFields 4 | { 5 | public const string Acl = "_acl"; 6 | public const string BestBets = "_bestbets"; 7 | public const string Id = "Id"; 8 | public const string Indexed = "Indexed"; 9 | public const string ParentId = "ParentId"; 10 | public const string Name = "Name"; 11 | public const string Type = "Type"; 12 | public const string Types = "Types"; 13 | public const string Path = "Path"; 14 | public const string AttachmentData = "_attachmentdata"; 15 | public const string AttachmentContent = "attachment.content"; 16 | public const string AttachmentAuthor = "attachment.author"; 17 | public const string AttachmentKeywords = "attachment.keywords"; 18 | public const string Suggest = "Suggest"; 19 | public const string StartPublish = "StartPublish"; 20 | public const string StopPublish = "StopPublish"; 21 | public const string Created = "Created"; 22 | public const string Changed = "Changed"; 23 | public const string Lang = "Lang"; 24 | public const string DidYouMean = "DidYouMean"; 25 | } 26 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/DefaultSearchLanguage.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using Epinova.ElasticSearch.Core.Contracts; 3 | using EPiServer.ServiceLocation; 4 | 5 | namespace Epinova.ElasticSearch.Core 6 | { 7 | [ServiceConfiguration(typeof(ISearchLanguage), Lifecycle = ServiceInstanceScope.Hybrid)] 8 | public class DefaultSearchLanguage : ISearchLanguage 9 | { 10 | public CultureInfo SearchLanguage => CultureInfo.CurrentCulture; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/ElasticSearchService.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Contracts; 2 | using Epinova.ElasticSearch.Core.Settings; 3 | using EPiServer.ServiceLocation; 4 | 5 | namespace Epinova.ElasticSearch.Core 6 | { 7 | [ServiceConfiguration(ServiceType = typeof(IElasticSearchService), Lifecycle = ServiceInstanceScope.Transient)] 8 | public class ElasticSearchService : ElasticSearchService, IElasticSearchService 9 | { 10 | public ElasticSearchService(IServerInfoService serverInfoService, IElasticSearchSettings settings, IHttpClientHelper httpClientHelper, ISearchLanguage searchLanguage) : base(serverInfoService, settings, httpClientHelper, searchLanguage) 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Enums/MappingConflict.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace Epinova.ElasticSearch.Core.Enums 5 | { 6 | [Flags] 7 | internal enum MappingConflict 8 | { 9 | NotSet = 0x0000, 10 | 11 | [Description("Mapping is ok")] 12 | Found = 0x0001, 13 | 14 | [Description("Mapping is missing")] 15 | Missing = 0x0010, 16 | 17 | [Description("Mapping is in conflict")] 18 | Mapping = 0x0100, 19 | 20 | [Description("Mapping has different analyzer")] 21 | Analyzer = 0x1000, 22 | } 23 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Enums/MappingType.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Enums 2 | { 3 | /// 4 | /// https://www.elastic.co/guide/en/elasticsearch/reference/1.7/mapping-core-types.html 5 | /// string, integer/long, float/double, boolean, date 6 | /// 7 | public enum MappingType 8 | { 9 | Text = 0, 10 | Attachment, 11 | Integer, 12 | Long, 13 | Float, 14 | Double, 15 | Boolean, 16 | Date, 17 | Geo_Point, 18 | Integer_Range, 19 | Object 20 | } 21 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Enums/Operator.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Epinova.ElasticSearch.Core.Enums 4 | { 5 | public enum Operator 6 | { 7 | [EnumMember(Value = "or")] 8 | Or, 9 | 10 | [EnumMember(Value = "and")] 11 | And 12 | } 13 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Epinova.ElasticSearch.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Epinova.ElasticSearch.Core 4 | Epinova AS 5 | Epinova.ElasticSearch.Core 6 | Epinova ElasticSearch EPiServer functionality $(Gitlog) 7 | $(GitContributors) @ Epinova 8 | Copyright© 2023 - Epinova AS 9 | false 10 | net472 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Events/BestBetEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Epinova.ElasticSearch.Core.Events 4 | { 5 | public class BestBetEventArgs : EventArgs 6 | { 7 | public string Index { get; set; } 8 | public long Id { get; set; } 9 | public string[] Terms { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Events/Events.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Events 2 | { 3 | public delegate void OnBeforeIndexContent(System.EventArgs e); 4 | public delegate void OnBeforeUpdateItem(IndexItemEventArgs e); 5 | public delegate void OnAfterUpdateBestBet(BestBetEventArgs e); 6 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Events/IndexItemEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Epinova.ElasticSearch.Core.Events 4 | { 5 | public class IndexItemEventArgs : EventArgs 6 | { 7 | public string CallerInfo { get; set; } 8 | public object Item { get; set; } 9 | public Type Type { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Extensions/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | namespace Epinova.ElasticSearch.Core.Extensions 8 | { 9 | public static class EnumExtensions 10 | { 11 | public static IEnumerable AsEnumDescriptions(this T instance) where T : Enum 12 | { 13 | return Enum.GetValues(typeof(T)).Cast() 14 | .Where(x => instance.HasFlag(x) && Convert.ToInt32(x) > 0) 15 | .DefaultIfEmpty() 16 | .Select(f => typeof(T).GetField(f.ToString()).GetCustomAttribute()?.Description ?? f.ToString()); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Extensions/SimpleSearchExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Runtime.Serialization; 5 | using Epinova.ElasticSearch.Core.Enums; 6 | 7 | namespace Epinova.ElasticSearch.Core.Extensions 8 | { 9 | public static class SimpleSearchExtensions 10 | { 11 | /// 12 | /// Gets the value as elastic searchs json format for flags. (OR|AND|PREFIX) 13 | /// 14 | /// Enum instance to format. 15 | /// Value in elastic search json format. 16 | internal static string AsJsonValue(this SimpleQuerystringOperators instance) 17 | { 18 | if (instance == SimpleQuerystringOperators.All) 19 | { 20 | return typeof(SimpleQuerystringOperators) 21 | .GetField(nameof(SimpleQuerystringOperators.All)) 22 | .GetCustomAttribute()?.Value ?? nameof(SimpleQuerystringOperators.All); 23 | } 24 | 25 | return string.Join("|", Enum.GetValues(typeof(SimpleQuerystringOperators)) 26 | .Cast() 27 | .Where(x => instance.HasFlag(x) && Convert.ToInt32(x) > 0) 28 | .DefaultIfEmpty() 29 | .Select(f => typeof(SimpleQuerystringOperators).GetField(f.ToString()).GetCustomAttribute()?.Value ?? f.ToString())); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Extensions/UrlExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Specialized; 3 | using System.Web; 4 | using System.Web.Mvc; 5 | using EPiServer; 6 | 7 | namespace Epinova.ElasticSearch.Core.Extensions 8 | { 9 | public static class UrlExtensions 10 | { 11 | public static string FacetFilterUrl(this UrlHelper instance, string facetName, object facetValue, bool replace = false, bool removeAllOtherFacets = false) 12 | { 13 | var ub = new UrlBuilder(HttpContext.Current.Request.Url); 14 | string facet = ub.QueryCollection[facetName]; 15 | ub.QueryCollection.Remove(facetName); 16 | 17 | if(removeAllOtherFacets) 18 | { 19 | ub.QueryCollection = new NameValueCollection(); 20 | } 21 | 22 | if(facetValue != null) 23 | { 24 | ub.QueryCollection.Add(facetName, replace || String.IsNullOrEmpty(facet) ? facetValue.ToString() : String.Concat(facet, "¤", facetValue)); 25 | } 26 | 27 | return ub.ToString(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Admin/Node.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Admin 4 | { 5 | public class Node 6 | { 7 | [JsonProperty("n")] 8 | public string Name { get; private set; } 9 | 10 | [JsonProperty("m")] 11 | private string MasterInternal { get; set; } 12 | 13 | [JsonIgnore] 14 | public bool Master => MasterInternal == "*"; 15 | 16 | [JsonProperty("v")] 17 | public string Version { get; private set; } 18 | 19 | [JsonProperty("http")] 20 | public string Ip { get; set; } 21 | 22 | [JsonProperty("d")] 23 | public string HddAvailable { get; private set; } 24 | 25 | [JsonProperty("rc")] 26 | public string MemoryCurrent { get; private set; } 27 | 28 | [JsonProperty("rm")] 29 | public string MemoryTotal { get; private set; } 30 | 31 | [JsonProperty("u")] 32 | public string Uptime { get; private set; } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Admin/Plugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Admin 5 | { 6 | public class Plugin 7 | { 8 | public Plugin() 9 | { 10 | } 11 | 12 | public Plugin(string component, string version) 13 | { 14 | Component = component; 15 | Version = new Version(version); 16 | } 17 | 18 | [JsonProperty(JsonNames.Component)] 19 | public string Component { get; set; } 20 | 21 | [JsonProperty(JsonNames.PluginVersion)] 22 | public Version Version { get; set; } = new Version(); 23 | 24 | public override string ToString() 25 | => $"{Component}: v{Version}"; 26 | } 27 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Admin/ServerInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Admin 5 | { 6 | public class ServerInfo 7 | { 8 | public string Name { get; set; } 9 | 10 | [JsonProperty(JsonNames.ClusterName)] 11 | public string Cluster { get; set; } 12 | 13 | [JsonIgnore] 14 | public Version Version => new Version(ElasticVersion.Number); 15 | 16 | [JsonIgnore] 17 | public Version LuceneVersion => new Version(ElasticVersion.LuceneVersion); 18 | 19 | [JsonProperty(JsonNames.NodeVersion)] 20 | internal InternalVersion ElasticVersion { get; set; } 21 | 22 | internal class InternalVersion 23 | { 24 | public string Number { get; set; } 25 | 26 | [JsonProperty(JsonNames.LuceneVersion)] 27 | public string LuceneVersion { get; set; } 28 | } 29 | 30 | public override string ToString() 31 | => $"{Name} ({Cluster}): v{Version}"; 32 | } 33 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Bulk/BulkBatchResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Bulk 4 | { 5 | public sealed class BulkBatchResult 6 | { 7 | public List Batches { get; } = new List(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Bulk/BulkMetadata.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Bulk 4 | { 5 | public class BulkMetadata : BulkMetadataBase 6 | { 7 | [JsonIgnore] 8 | public Operation Operation { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Bulk/BulkMetadataBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Bulk 5 | { 6 | public class BulkMetadataBase 7 | { 8 | [JsonProperty(JsonNames.Index)] 9 | public string Index { get; set; } 10 | 11 | [JsonIgnore] 12 | public string IndexCandidate { get; set; } 13 | 14 | [JsonProperty(JsonNames.Error)] 15 | public Error Error { get; set; } 16 | 17 | [JsonIgnore] 18 | public Type DataType { get; set; } 19 | 20 | [JsonProperty(JsonNames.Id)] 21 | public long Id { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Bulk/BulkResult.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Bulk 4 | { 5 | public sealed class BulkResult 6 | { 7 | public int Took { get; set; } 8 | 9 | [JsonProperty("ingest_took")] 10 | public int IngestTook { get; set; } 11 | 12 | public bool Errors { get; set; } 13 | 14 | public BulkResultItem[] Items { get; set; } = new BulkResultItem[0]; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Bulk/BulkResultItem.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Models.Converters; 2 | using Newtonsoft.Json; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Bulk 5 | { 6 | [JsonConverter(typeof(BulkResultItemConverter))] 7 | public class BulkResultItem : BulkMetadataBase 8 | { 9 | public void Populate(BulkMetadataBase metadata, BulkResultItemStatus status) 10 | { 11 | Id = metadata.Id; 12 | Index = metadata.Index; 13 | Status = status.Status; 14 | Version = status.Version; 15 | Error = metadata.Error; 16 | } 17 | 18 | public Operation Operation { get; set; } 19 | 20 | public int Version { get; set; } 21 | 22 | public int Status { get; set; } 23 | 24 | public override string ToString() 25 | { 26 | var result = $"Status: {Status}\nId: {Id}\nError: {Error?.Reason}"; 27 | 28 | if(Error?.Header?.Processor != null) 29 | { 30 | result += $"\nProcessor: {Error?.Header?.Processor}"; 31 | } 32 | 33 | return result; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Bulk/BulkResultItemStatus.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Bulk 4 | { 5 | public class BulkResultItemStatus 6 | { 7 | public int Status { get; set; } 8 | 9 | [JsonProperty(JsonNames.Version)] 10 | public int Version { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Bulk/Error.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Bulk 4 | { 5 | public class Error 6 | { 7 | [JsonProperty(JsonNames.RootCause)] 8 | public ErrorRootCause[] RootCause { get; set; } 9 | 10 | [JsonProperty(JsonNames.Type)] 11 | public string Type { get; set; } 12 | 13 | [JsonProperty(JsonNames.Reason)] 14 | public string Reason { get; set; } 15 | 16 | [JsonProperty(JsonNames.Cause)] 17 | public Cause CausedBy { get; set; } 18 | 19 | public HeaderInfo Header { get; set; } 20 | 21 | [JsonProperty(JsonNames.StackTrace)] 22 | public string StackTrace { get; set; } 23 | 24 | 25 | public class Cause 26 | { 27 | [JsonProperty(JsonNames.Type)] 28 | public string Type { get; set; } 29 | 30 | [JsonProperty(JsonNames.Reason)] 31 | public string Reason { get; set; } 32 | } 33 | 34 | public class HeaderInfo 35 | { 36 | [JsonProperty("processor_type")] 37 | public string Processor { get; set; } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Bulk/ErrorRootCause.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Bulk 4 | { 5 | public class ErrorRootCause 6 | { 7 | [JsonProperty(JsonNames.Type)] 8 | public string Type { get; set; } 9 | 10 | [JsonProperty(JsonNames.Reason)] 11 | public string Reason { get; set; } 12 | 13 | [JsonProperty(JsonNames.StackTrace)] 14 | public string StackTrace { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Bulk/Operation.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models.Bulk 2 | { 3 | public enum Operation 4 | { 5 | Index = 0, 6 | Create, 7 | Update, 8 | Delete 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models 2 | { 3 | internal static class Constants 4 | { 5 | public const string KeywordSuffix = ".keyword"; 6 | public const string MoreLikeThisId = nameof(MoreLikeThisId); 7 | } 8 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Converters/AggregationConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Epinova.ElasticSearch.Core.Models.Query; 4 | using Newtonsoft.Json; 5 | 6 | namespace Epinova.ElasticSearch.Core.Models.Converters 7 | { 8 | public class AggregationConverter : JsonConverter 9 | { 10 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 11 | => null; 12 | 13 | public override bool CanConvert(Type objectType) 14 | => objectType == typeof(Dictionary); 15 | 16 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 17 | { 18 | var buckets = value as Dictionary; 19 | if(buckets == null) 20 | { 21 | return; 22 | } 23 | 24 | writer.WriteStartObject(); 25 | 26 | foreach(KeyValuePair bucket in buckets) 27 | { 28 | string name = bucket.Value.Terms.Field.Replace(Constants.KeywordSuffix, String.Empty); 29 | 30 | writer.WritePropertyName(name); 31 | serializer.Serialize(writer, bucket.Value); 32 | } 33 | 34 | writer.WriteEnd(); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Converters/BucketConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Epinova.ElasticSearch.Core.Models.Query; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models.Converters 6 | { 7 | public class BucketConverter : JsonConverter 8 | { 9 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 10 | => null; 11 | 12 | public override bool CanConvert(Type objectType) 13 | => objectType == typeof(Bucket); 14 | 15 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 16 | { 17 | var bucket = value as Bucket; 18 | if(bucket == null) 19 | { 20 | return; 21 | } 22 | 23 | writer.WriteStartObject(); 24 | serializer.Serialize(writer, bucket); 25 | writer.WriteEnd(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Converters/BulkMetadataConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Epinova.ElasticSearch.Core.Models.Bulk; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models.Converters 6 | { 7 | public class BulkMetadataConverter : JsonConverter 8 | { 9 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => null; 10 | 11 | public override bool CanConvert(Type objectType) => objectType == typeof(BulkMetadata); 12 | 13 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 14 | { 15 | BulkMetadata bulk = value as BulkMetadata; 16 | if(bulk == null) 17 | return; 18 | 19 | string name = Enum.GetName(typeof(Operation), bulk.Operation); 20 | if(name == null) 21 | return; 22 | 23 | writer.WriteStartObject(); 24 | writer.WritePropertyName(name.ToLower()); 25 | serializer.Converters.Remove(this); 26 | serializer.Serialize(writer, bulk); 27 | serializer.Converters.Add(this); 28 | writer.WriteEndObject(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Converters/BulkResultItemConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Epinova.ElasticSearch.Core.Models.Bulk; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace Epinova.ElasticSearch.Core.Models.Converters 8 | { 9 | public class BulkResultItemConverter : JsonConverter 10 | { 11 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 12 | { 13 | JObject jo = JObject.Load(reader); 14 | 15 | string operationRaw = jo.Properties().First().Name; 16 | 17 | BulkMetadataBase metadata = jo[operationRaw].ToObject(); 18 | BulkResultItemStatus status = jo[operationRaw].ToObject(); 19 | 20 | var item = new BulkResultItem(); 21 | item.Populate(metadata, status); 22 | 23 | Enum.TryParse(operationRaw, true, out Operation operation); 24 | item.Operation = operation; 25 | 26 | return item; 27 | } 28 | 29 | public override bool CanConvert(Type objectType) 30 | => objectType == typeof(BulkResultItem); 31 | 32 | public override bool CanWrite => false; 33 | 34 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 35 | => throw new NotImplementedException(); 36 | } 37 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Converters/GeoDistanceConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Epinova.ElasticSearch.Core.Models.Query; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models.Converters 6 | { 7 | public class GeoDistanceConverter : JsonConverter 8 | { 9 | public override bool CanRead => false; 10 | 11 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 12 | => null; 13 | 14 | public override bool CanConvert(Type objectType) 15 | => typeof(MatchBase).IsAssignableFrom(objectType); 16 | 17 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 18 | { 19 | var geoDistance = value as GeoDistance; 20 | 21 | if(geoDistance?.Point == null) 22 | { 23 | return; 24 | } 25 | 26 | writer.WriteStartObject(); 27 | writer.WritePropertyName(JsonNames.GeoDistance); 28 | writer.WriteStartObject(); 29 | writer.WritePropertyName(JsonNames.Distance); 30 | writer.WriteValue(geoDistance.Distance); 31 | writer.WritePropertyName(geoDistance.Field); 32 | writer.WriteStartObject(); 33 | writer.WritePropertyName(JsonNames.Lat); 34 | writer.WriteValue(geoDistance.Point.Lat); 35 | writer.WritePropertyName(JsonNames.Lon); 36 | writer.WriteValue(geoDistance.Point.Lon); 37 | writer.WriteEndObject(); 38 | writer.WriteEndObject(); 39 | writer.WriteEnd(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Converters/GeoPointConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Epinova.ElasticSearch.Core.Models.Properties; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models.Converters 6 | { 7 | public class GeoPointConverter : JsonConverter 8 | { 9 | public override bool CanConvert(Type objectType) 10 | => objectType == typeof(GeoPoint); 11 | 12 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 13 | => GeoPoint.Parse(reader.Value as string); 14 | 15 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 16 | { 17 | var geoPoint = value as GeoPoint; 18 | 19 | if(geoPoint == null) 20 | { 21 | return; 22 | } 23 | 24 | writer.WriteStartObject(); 25 | writer.WritePropertyName(JsonNames.Lat); 26 | writer.WriteValue(geoPoint.Lat); 27 | writer.WritePropertyName(JsonNames.Lon); 28 | writer.WriteValue(geoPoint.Lon); 29 | writer.WriteEnd(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/CustomSearchHit.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models 2 | { 3 | public sealed class CustomSearchHit 4 | { 5 | public T Item { get; } 6 | public double Score { get; } 7 | public string Highlight { get; } 8 | 9 | public CustomSearchHit(T item, double queryScore, string highlight) 10 | { 11 | Item = item; 12 | Score = queryScore; 13 | Highlight = highlight; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/CustomSearchResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models 5 | { 6 | /// 7 | /// Contains the materialization of the search query 8 | /// 9 | public sealed class CustomSearchResult : SearchResultBase> 10 | { 11 | public CustomSearchResult() 12 | { 13 | } 14 | 15 | public CustomSearchResult(SearchResult searchResult, IEnumerable> filteredHits) 16 | { 17 | Query = searchResult.Query; 18 | Took = searchResult.Took; 19 | Facets = searchResult.Facets; 20 | Hits = filteredHits ?? Enumerable.Empty>(); 21 | TotalHits = searchResult.TotalHits; 22 | DidYouMeanSuggestions = searchResult.DidYouMeanSuggestions; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Facet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models 5 | { 6 | internal sealed class Facet 7 | { 8 | [JsonProperty(JsonNames.Key)] 9 | private string KeyDefault { get; set; } 10 | 11 | [JsonProperty(JsonNames.KeyAsString)] 12 | private string KeyAsString { get; set; } 13 | 14 | public string Key 15 | { 16 | get 17 | { 18 | if(!String.IsNullOrWhiteSpace(KeyAsString)) 19 | { 20 | return KeyAsString; 21 | } 22 | 23 | return KeyDefault; 24 | } 25 | } 26 | 27 | [JsonProperty(JsonNames.DocCount)] 28 | public int Count { get; set; } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/FacetEntry.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models 2 | { 3 | public sealed class FacetEntry 4 | { 5 | public FacetEntry() 6 | { 7 | Hits = new FacetHit[0]; 8 | } 9 | 10 | public string Key { get; set; } 11 | 12 | public int Count { get; set; } 13 | 14 | public FacetHit[] Hits { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/FacetHit.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models 2 | { 3 | public sealed class FacetHit 4 | { 5 | public string Key { get; set; } 6 | 7 | public int Count { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Filter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Epinova.ElasticSearch.Core.Enums; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models 6 | { 7 | internal sealed class Filter 8 | { 9 | public Filter(string fieldName, object value, Type type, bool raw, Operator @operator, bool not = false) 10 | { 11 | Operator = @operator; 12 | FieldName = fieldName; 13 | Value = value; 14 | Type = type; 15 | Raw = raw; 16 | Not = not; 17 | } 18 | 19 | 20 | public string FieldName { get; private set; } 21 | 22 | public object Value { get; private set; } 23 | 24 | public Type Type { get; private set; } 25 | 26 | public bool Raw { get; private set; } 27 | 28 | [JsonIgnore] 29 | public Operator Operator { get; private set; } 30 | 31 | [JsonIgnore] 32 | public bool Not { get; private set; } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/FilterGroupQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.Enums; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models 6 | { 7 | internal sealed class FilterGroupQuery 8 | { 9 | public FilterGroupQuery(Operator @operator = Operator.And) 10 | { 11 | Operator = @operator; 12 | Filters = new List(); 13 | } 14 | 15 | 16 | [JsonIgnore] 17 | public List Filters { get; private set; } 18 | 19 | [JsonIgnore] 20 | public Operator Operator { get; private set; } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/IndexableProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models 4 | { 5 | internal sealed class IndexableProperty 6 | { 7 | public string Name { get; set; } 8 | 9 | public Type Type { get; set; } 10 | 11 | public bool Analyzable { get; set; } 12 | 13 | public bool IncludeInDidYouMean { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/InspectItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Epinova.ElasticSearch.Core.Extensions; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace Epinova.ElasticSearch.Core.Models 7 | { 8 | public class InspectItem 9 | { 10 | public string Title { get; } 11 | 12 | public string ShortTypeName { get; } 13 | 14 | public string Content { get; } 15 | 16 | public InspectItem(JToken token) 17 | { 18 | JObject instance = token.Value().Property("_source").Value as JObject; 19 | 20 | if(instance == null) 21 | { 22 | return; 23 | } 24 | 25 | Title = GetTitle(instance); 26 | string type = instance.Property("Type")?.Value.ToString(); 27 | ShortTypeName = type.GetShortTypeName(); 28 | Content = instance.ToString(Formatting.Indented); 29 | } 30 | 31 | private string GetTitle(JObject instance) 32 | { 33 | string propertyValue = GetPropertyValue(instance, "Name"); 34 | if(!String.IsNullOrWhiteSpace(propertyValue)) 35 | { 36 | return propertyValue; 37 | } 38 | 39 | propertyValue = GetPropertyValue(instance, "Title"); 40 | if(!String.IsNullOrWhiteSpace(propertyValue)) 41 | { 42 | return propertyValue; 43 | } 44 | 45 | return "Name or Title should be indexed."; 46 | } 47 | 48 | private string GetPropertyValue(JObject instance, string propertyName) 49 | { 50 | JProperty property = instance.Property(propertyName); 51 | return property?.Value.ToString(); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Mapping/MappingValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Mapping 4 | { 5 | public class MappingValidatorType 6 | { 7 | public string Name { get; } 8 | public List Properties { get; } 9 | 10 | public MappingValidatorType(string name, List properties) 11 | { 12 | Name = name; 13 | Properties = properties; 14 | } 15 | } 16 | 17 | public class MappingValidatorProperty 18 | { 19 | public MappingValidatorProperty(string propertyName, IEnumerable errors) 20 | { 21 | PropertyName = propertyName; 22 | Errors = errors; 23 | } 24 | 25 | public string PropertyName { get; } 26 | public IEnumerable Errors { get; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Properties/IntegerRange.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Contracts; 2 | using Newtonsoft.Json; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Properties 5 | { 6 | public class IntegerRange : IProperty 7 | { 8 | public IntegerRange(int gte, int lte) 9 | { 10 | Gte = gte; 11 | Lte = lte; 12 | } 13 | 14 | 15 | [JsonProperty(JsonNames.Gte)] 16 | public int Gte { get; set; } 17 | 18 | [JsonProperty(JsonNames.Lte)] 19 | public int Lte { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/AggregationTerms.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal class AggregationTerms 6 | { 7 | [JsonProperty(JsonNames.Field)] 8 | public string Field { get; set; } 9 | 10 | [JsonProperty(JsonNames.Size)] 11 | public int Size => 1000; 12 | } 13 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/BestBetsRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal class BestBetsRequest 6 | { 7 | public BestBetsRequest(string[] bestbets) 8 | { 9 | Document = new Doc 10 | { 11 | BestBets = bestbets 12 | }; 13 | } 14 | 15 | [JsonProperty(JsonNames.Doc)] 16 | public Doc Document { get; private set; } 17 | 18 | 19 | internal class Doc 20 | { 21 | [JsonProperty(DefaultFields.BestBets)] 22 | public string[] BestBets { get; set; } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/BoolQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Query 5 | { 6 | internal class BoolQuery 7 | { 8 | [JsonProperty(JsonNames.Must)] 9 | public List Must { get; set; } = new List(); 10 | 11 | [JsonProperty(JsonNames.MustNot)] 12 | public List MustNot { get; set; } = new List(); 13 | 14 | [JsonProperty(JsonNames.Should)] 15 | public List Should { get; set; } = new List(); 16 | 17 | [JsonProperty(JsonNames.MinimumNumberShouldMatch)] 18 | public int? MinimumNumberShouldMatch { get; set; } 19 | 20 | [JsonProperty(JsonNames.Filter)] 21 | public List Filter { get; set; } = new List(); 22 | 23 | public bool ShouldSerializeMust() 24 | => Must?.Count > 0; 25 | 26 | public bool ShouldSerializeMustNot() 27 | => MustNot?.Count > 0; 28 | 29 | public bool ShouldSerializeShould() 30 | => Should?.Count > 0; 31 | 32 | public bool ShouldSerializeFilter() 33 | => Filter?.Count > 0; 34 | } 35 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Boost.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models.Query 2 | { 3 | internal sealed class Boost 4 | { 5 | public string FieldName { get; set; } 6 | public int Weight { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Bucket.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal class Bucket 6 | { 7 | public Bucket(string field) 8 | { 9 | Terms = new AggregationTerms { Field = field }; 10 | } 11 | 12 | [JsonProperty(JsonNames.Terms)] 13 | public AggregationTerms Terms { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Completion.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal sealed class Completion 6 | { 7 | [JsonProperty(JsonNames.Field)] 8 | public string Field { get; set; } 9 | 10 | [JsonProperty(JsonNames.Size)] 11 | public int Size { get; set; } 12 | 13 | [JsonProperty(JsonNames.SkipDuplicates)] 14 | public bool? SkipDuplicates { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/DidYouMeanSuggest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal class DidYouMeanSuggest 6 | { 7 | public DidYouMeanSuggest(string text) 8 | { 9 | DidYouMean = new DidYouMeanInternal { Text = text }; 10 | } 11 | 12 | [JsonProperty(JsonNames.DidYouMean)] 13 | public DidYouMeanInternal DidYouMean { get; set; } 14 | 15 | 16 | internal class DidYouMeanInternal 17 | { 18 | public DidYouMeanInternal() 19 | { 20 | Phrase = new PhraseInternal(); 21 | } 22 | 23 | [JsonProperty(JsonNames.Text)] 24 | public string Text { get; set; } 25 | 26 | [JsonProperty(JsonNames.Phrase)] 27 | public PhraseInternal Phrase { get; set; } 28 | 29 | 30 | internal class PhraseInternal 31 | { 32 | [JsonProperty(JsonNames.Field)] 33 | public string Field => DefaultFields.DidYouMean; 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Gauss.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models.Query 2 | { 3 | internal sealed class Gauss 4 | { 5 | public string Field { get; set; } 6 | public string Scale { get; set; } 7 | public string Offset { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/GeoBoundingBox.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Models.Converters; 2 | using Epinova.ElasticSearch.Core.Models.Properties; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models.Query 6 | { 7 | [JsonConverter(typeof(GeoBoundingBoxConverter))] 8 | internal class GeoBoundingBox : MatchBase 9 | { 10 | public GeoBoundingBox(string field, GeoPoint topLeft, GeoPoint bottomRight) 11 | { 12 | Field = field; 13 | Box = new BoundingBox 14 | { 15 | TopLeft = topLeft, 16 | BottomRight = bottomRight 17 | }; 18 | } 19 | 20 | [JsonIgnore] 21 | public string Field { get; set; } 22 | 23 | [JsonProperty(JsonNames.GeoBoundingBox)] 24 | public BoundingBox Box { get; } 25 | 26 | internal class BoundingBox 27 | { 28 | [JsonProperty(JsonNames.TopLeft)] 29 | public GeoPoint TopLeft { get; set; } 30 | 31 | [JsonProperty(JsonNames.BottomRight)] 32 | public GeoPoint BottomRight { get; set; } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/GeoDistance.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Models.Converters; 2 | using Epinova.ElasticSearch.Core.Models.Properties; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models.Query 6 | { 7 | [JsonConverter(typeof(GeoDistanceConverter))] 8 | internal class GeoDistance : MatchBase 9 | { 10 | public GeoDistance(string field, string distance, GeoPoint center) 11 | { 12 | Field = field; 13 | Distance = distance; 14 | Point = center; 15 | } 16 | 17 | public string Field { get; set; } 18 | 19 | public string Distance { get; set; } 20 | 21 | public GeoPoint Point { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/GeoPolygon.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.Models.Converters; 3 | using Epinova.ElasticSearch.Core.Models.Properties; 4 | using Newtonsoft.Json; 5 | 6 | namespace Epinova.ElasticSearch.Core.Models.Query 7 | { 8 | [JsonConverter(typeof(GeoPolygonConverter))] 9 | internal class GeoPolygon : MatchBase 10 | { 11 | public GeoPolygon(string field, IEnumerable points) 12 | { 13 | Field = field; 14 | Points = points; 15 | } 16 | 17 | [JsonIgnore] 18 | public string Field { get; set; } 19 | 20 | [JsonIgnore] 21 | public IEnumerable Points { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/GeoSort.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models.Query 2 | { 3 | internal class GeoSort : Sort 4 | { 5 | public (double Lat, double Lon) CompareTo { get; set; } 6 | 7 | public string Unit { get; set; } 8 | 9 | public string Mode { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Highlight.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models.Query 6 | { 7 | internal sealed class Highlight 8 | { 9 | public Highlight() 10 | { 11 | Fields = new Dictionary(); 12 | } 13 | 14 | [JsonProperty(JsonNames.Order)] 15 | public string Order => "score"; 16 | 17 | [JsonProperty(JsonNames.PreTags)] 18 | public string[] PreTags 19 | { 20 | get 21 | { 22 | string tag = Conventions.Indexing.HighlightTag; 23 | if(String.IsNullOrEmpty(tag)) 24 | { 25 | return new[] { String.Empty }; 26 | } 27 | 28 | return new[] { $"<{tag}>" }; 29 | } 30 | } 31 | 32 | [JsonProperty(JsonNames.PostTags)] 33 | public string[] PostTags 34 | { 35 | get 36 | { 37 | string tag = Conventions.Indexing.HighlightTag; 38 | if(String.IsNullOrEmpty(tag)) 39 | { 40 | return new[] { String.Empty }; 41 | } 42 | 43 | return new[] { $"" }; 44 | } 45 | } 46 | 47 | [JsonProperty(JsonNames.Fields)] 48 | public Dictionary Fields { get; set; } 49 | } 50 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Match.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Enums; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Converters; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models.Query 6 | { 7 | internal class Match : MatchBase 8 | { 9 | public class MatchInternal 10 | { 11 | } 12 | 13 | public class All 14 | { 15 | [JsonProperty(JsonNames.Query)] 16 | public string Query { get; set; } 17 | 18 | [JsonConverter(typeof(StringEnumConverter))] 19 | [JsonProperty(JsonNames.Operator)] 20 | public Operator Operator { get; set; } 21 | 22 | [JsonProperty(JsonNames.Lenient)] 23 | public bool Lenient => true; 24 | } 25 | 26 | public Match(string query, Operator @operator) 27 | { 28 | MatchQuery = new MatchInternal(); 29 | } 30 | 31 | [JsonProperty(JsonNames.Match)] 32 | public MatchInternal MatchQuery { get; private set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/MatchAll.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Query 5 | { 6 | internal class MatchAll : MatchBase 7 | { 8 | [JsonProperty(JsonNames.MatchAll)] 9 | public JObject Match { get; } = new JObject(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/MatchBase.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models.Query 2 | { 3 | //TODO: Should this be an interface? Check IsAssignableFrom in RangeConverter if converting 4 | internal abstract class MatchBase { } 5 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/MatchSimple.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Query 5 | { 6 | internal class MatchSimple : MatchBase 7 | { 8 | public MatchSimple(string field, string query) 9 | { 10 | Match = new JObject(new JProperty(field, query)); 11 | } 12 | 13 | [JsonProperty(JsonNames.Match)] 14 | public JObject Match { get; private set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/MatchWithBoost.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Enums; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models.Query 6 | { 7 | internal class MatchWithBoost : MatchBase 8 | { 9 | public MatchWithBoost(string field, string query, int boost, Operator @operator) 10 | { 11 | Match = new JObject( 12 | new JProperty(field, new JObject( 13 | new JProperty(JsonNames.Query, query), 14 | new JProperty(JsonNames.Boost, boost), 15 | new JProperty(JsonNames.Lenient, true), 16 | new JProperty(JsonNames.Operator, @operator.ToString().ToLower()) 17 | )) 18 | ); 19 | } 20 | 21 | [JsonProperty(JsonNames.Match)] 22 | public JObject Match { get; private set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/MoreLikeThisQuery.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal class MoreLikeThisQuery : QueryBase 6 | { 7 | [JsonProperty(JsonNames.MoreLikeThis)] 8 | public MoreLikeThisQueryInternal QueryInternal { get; set; } = new MoreLikeThisQueryInternal(); 9 | 10 | internal class MoreLikeThisQueryInternal 11 | { 12 | [JsonProperty(JsonNames.Like)] 13 | public LikeQuery[] Like { get; set; } 14 | 15 | [JsonProperty(JsonNames.MinTermFreq)] 16 | public int MinTermFreq { get; set; } = 1; 17 | 18 | [JsonProperty(JsonNames.MaxQueryTerms)] 19 | public int MaxQueryTerms { get; set; } = 12; 20 | 21 | [JsonProperty(JsonNames.MinDocFreq)] 22 | public int MinDocFreq { get; set; } = 12; 23 | 24 | [JsonProperty(JsonNames.MinWordLength)] 25 | public int MinWordLength { get; set; } = 12; 26 | 27 | [JsonProperty(JsonNames.Fields)] 28 | public string[] Fields { get; set; } 29 | } 30 | 31 | internal class LikeQuery 32 | { 33 | [JsonProperty(JsonNames.Id)] 34 | public string Id { get; set; } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Must.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal sealed class Must 6 | { 7 | public class MustInternal 8 | { 9 | [JsonProperty(JsonNames.MultiMatch)] 10 | public MatchMulti MultiMatch { get; set; } 11 | 12 | public Match Match { get; set; } 13 | 14 | public string Term { get; set; } 15 | } 16 | 17 | public Must(Match match, string term) 18 | { 19 | MustBody = new MustInternal 20 | { 21 | Match = match, 22 | Term = term 23 | }; 24 | } 25 | 26 | [JsonProperty(JsonNames.Must)] 27 | public MustInternal MustBody { get; private set; } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/NestedBoolQuery.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal class NestedBoolQuery : MatchBase 6 | { 7 | public NestedBoolQuery() : this(new BoolQuery()) 8 | { 9 | } 10 | 11 | public NestedBoolQuery(BoolQuery inner) 12 | { 13 | Bool = inner; 14 | } 15 | 16 | [JsonProperty(JsonNames.Bool)] 17 | public BoolQuery Bool { get; set; } 18 | 19 | public bool ShouldSerializeBool() => HasAnyValues(); 20 | 21 | public bool HasAnyValues() => 22 | Bool != null 23 | && (Bool.ShouldSerializeFilter() 24 | || Bool.ShouldSerializeMust() 25 | || Bool.ShouldSerializeMustNot() 26 | || Bool.ShouldSerializeShould()); 27 | } 28 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/PostFilter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal class PostFilter : QueryBase 6 | { 7 | public override string ToString() 8 | { 9 | return JsonConvert.SerializeObject( 10 | this, 11 | Formatting.Indented, 12 | new JsonSerializerSettings 13 | { 14 | NullValueHandling = NullValueHandling.Ignore, 15 | ReferenceLoopHandling = ReferenceLoopHandling.Ignore 16 | }); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Query.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Query 5 | { 6 | internal class Query : QueryBase 7 | { 8 | [JsonIgnore] 9 | public List SearchFields { get; set; } = new List(); 10 | 11 | [JsonIgnore] 12 | public string SearchText { get; set; } 13 | 14 | [JsonProperty(JsonNames.Query)] 15 | public FunctionScoreQuery FunctionScoreQuery { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/QueryBase.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal class QueryBase 6 | { 7 | [JsonProperty(JsonNames.Bool)] 8 | public BoolQuery Bool { get; set; } = new BoolQuery(); 9 | 10 | public bool ShouldSerializeBool() 11 | { 12 | return Bool != null 13 | && (Bool.ShouldSerializeFilter() 14 | || Bool.ShouldSerializeMust() 15 | || Bool.ShouldSerializeMustNot() 16 | || Bool.ShouldSerializeShould()); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/RangeBase.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models.Query 2 | { 3 | internal abstract class RangeBase : MatchBase { } 4 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/ScriptScore.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal sealed class ScriptScore 6 | { 7 | [JsonProperty(JsonNames.Script)] 8 | public ScriptScoreInner Script { get; set; } = new ScriptScoreInner(); 9 | 10 | 11 | internal sealed class ScriptScoreInner 12 | { 13 | [JsonProperty(JsonNames.Lang)] 14 | public string Language { get; set; } 15 | 16 | [JsonProperty(JsonNames.ScriptSource)] 17 | public string Source { get; set; } 18 | 19 | [JsonProperty(JsonNames.Inline)] 20 | public string Inline { get; set; } 21 | 22 | [JsonProperty(JsonNames.Params)] 23 | public object Parameters { get; set; } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/ScriptSort.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models.Query 2 | { 3 | internal class ScriptSort : Sort 4 | { 5 | public string Type { get; set; } 6 | 7 | public string Script { get; set; } 8 | 9 | public string Language { get; set; } 10 | 11 | public object Parameters { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Sort.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Enums; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal class Sort 6 | { 7 | public MappingType MappingType { get; set; } 8 | 9 | public string FieldName { get; set; } 10 | 11 | public string Direction { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/SuggestRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal sealed class SuggestRequest : RequestBase 6 | { 7 | public SuggestRequest(string query, int size) 8 | { 9 | Suggestions = new SuggestionsWrapper 10 | { 11 | Suggestions = new Suggestions 12 | { 13 | Text = query, 14 | Completion = new Completion 15 | { 16 | Field = DefaultFields.Suggest, 17 | SkipDuplicates = (bool?)true, 18 | Size = size > 0 ? size : 5 19 | } 20 | } 21 | }; 22 | } 23 | 24 | [JsonProperty(JsonNames.Source)] 25 | public bool Source => false; 26 | 27 | [JsonIgnore] 28 | public override int From { get; internal set; } 29 | 30 | [JsonIgnore] 31 | public override int Size { get; internal set; } 32 | 33 | public override bool ShouldSerializeFrom() => false; 34 | 35 | public override bool ShouldSerializeSize() => false; 36 | 37 | [JsonProperty(JsonNames.Suggest)] 38 | internal SuggestionsWrapper Suggestions { get; set; } 39 | 40 | internal class SuggestionsWrapper 41 | { 42 | public Suggestions Suggestions { get; set; } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Suggestions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Query 4 | { 5 | internal sealed class Suggestions 6 | { 7 | [JsonProperty(JsonNames.Text)] 8 | public string Text { get; set; } 9 | 10 | [JsonProperty(JsonNames.Completion)] 11 | public Completion Completion { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Terms.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Query 5 | { 6 | internal class Terms : Term 7 | { 8 | public Terms(string key, object value, bool nonRaw = false, Type dataType = null) : base(key, value, nonRaw, dataType) 9 | { 10 | TermsItem = new TermItem 11 | { 12 | Key = key, 13 | Value = value, 14 | NonRaw = nonRaw, 15 | Type = dataType 16 | }; 17 | 18 | TermItem = null; 19 | } 20 | 21 | [JsonProperty(JsonNames.Terms)] 22 | public TermItem TermsItem { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Query/Wildcard.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Query 5 | { 6 | internal class Wildcard : MatchBase 7 | { 8 | public Wildcard(string field, string query, sbyte boost = 0) 9 | { 10 | if(boost == 0) 11 | { 12 | WildcardQuery = new JObject(new JProperty(field, query)); 13 | } 14 | else 15 | { 16 | WildcardQuery = new JObject( 17 | new JProperty( 18 | field, 19 | new JObject( 20 | new JProperty("value", query), 21 | new JProperty("boost", boost) 22 | ))); 23 | } 24 | } 25 | 26 | [JsonProperty(JsonNames.Wildcard)] 27 | public JObject WildcardQuery { get; private set; } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/RawResults.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models 2 | { 3 | public class RawResults 4 | { 5 | public T RootObject { get; set; } 6 | 7 | public string RawJson { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/RequestBase.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models 4 | { 5 | internal abstract class RequestBase 6 | { 7 | //TODO: Should this be virtual? 8 | [JsonProperty(JsonNames.From)] 9 | public abstract int From { get; internal set; } 10 | 11 | //TODO: Should this be virtual? 12 | [JsonProperty(JsonNames.Size)] 13 | public abstract int Size { get; internal set; } 14 | 15 | [JsonIgnore] 16 | public bool IsPartOfFilteredQuery { get; set; } 17 | 18 | public bool ShouldSerializeFilter() => IsPartOfFilteredQuery; 19 | 20 | public virtual bool ShouldSerializeFrom() => !IsPartOfFilteredQuery; 21 | 22 | public virtual bool ShouldSerializeSize() => !IsPartOfFilteredQuery; 23 | 24 | public virtual string ToString(Formatting formatting) 25 | { 26 | return JsonConvert.SerializeObject( 27 | this, 28 | formatting, 29 | new JsonSerializerSettings 30 | { 31 | NullValueHandling = NullValueHandling.Ignore, 32 | ReferenceLoopHandling = ReferenceLoopHandling.Ignore 33 | }); 34 | } 35 | 36 | public override string ToString() => ToString(Formatting.None); 37 | } 38 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/SearchHit.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Epinova.ElasticSearch.Core.Models.Serialization; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models 5 | { 6 | public class SearchHit : IndexItem 7 | { 8 | internal SearchHit(Hit hit) 9 | { 10 | Id = hit.Source.Id; 11 | ParentId = hit.Source.ParentId; 12 | Lang = hit.Source.Lang; 13 | Title = hit.Source.Title; 14 | Name = hit.Source.Name; 15 | Updated = hit.Source.Updated; 16 | Type = hit.Source.Type; 17 | Types = hit.Source.Types; 18 | QueryScore = hit.Score; 19 | Highlight = hit.Highlight; 20 | CustomProperties = new Dictionary(); 21 | } 22 | 23 | public Dictionary CustomProperties { get; set; } 24 | 25 | public double QueryScore { get; private set; } 26 | 27 | public string Highlight { get; private set; } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/SearchResult.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models 2 | { 3 | /// 4 | /// Contains the materialization of the search query 5 | /// 6 | public class SearchResult : SearchResultBase 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/Aggregation.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Serialization 4 | { 5 | internal class Aggregation 6 | { 7 | [JsonProperty(JsonNames.Buckets)] 8 | public Facet[] Facets { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/DidYouMeanHit.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Serialization 4 | { 5 | internal class DidYouMeanHit 6 | { 7 | [JsonProperty(JsonNames.Text)] 8 | public string Text { get; set; } 9 | 10 | [JsonProperty(JsonNames.Options)] 11 | public DidYouMeanOption[] Options { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/DidYouMeanOption.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Serialization 4 | { 5 | public class DidYouMeanOption 6 | { 7 | [JsonProperty(JsonNames.Text)] 8 | public string Text { get; set; } 9 | 10 | [JsonProperty(JsonNames.Score)] 11 | public double Score { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/EsRootObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace Epinova.ElasticSearch.Core.Models.Serialization 5 | { 6 | internal class EsRootObjectBase 7 | { 8 | public int Took { get; set; } 9 | 10 | [JsonProperty(JsonNames.TimedOut)] 11 | public bool TimedOut { get; set; } 12 | 13 | [JsonProperty(JsonNames.Aggregations)] 14 | public Dictionary Aggregations { get; set; } 15 | 16 | [JsonProperty(JsonNames.Hits)] 17 | public T Hits { get; set; } 18 | 19 | [JsonProperty(JsonNames.Suggest)] 20 | public Suggest Suggest { get; set; } 21 | } 22 | 23 | internal class EsRootObject : EsRootObjectBase 24 | { 25 | } 26 | 27 | internal class EsCustomRootObject : EsRootObjectBase> 28 | { 29 | } 30 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/Hit.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Models.Serialization 6 | { 7 | internal class HitBase 8 | { 9 | [JsonProperty(JsonNames.Index)] 10 | public string Index { get; set; } 11 | 12 | [JsonProperty(JsonNames.HitType)] 13 | public string Type { get; set; } 14 | 15 | [JsonProperty(JsonNames.Id)] 16 | public string Id { get; set; } 17 | 18 | [JsonProperty(JsonNames.ScoreRoot)] 19 | public double Score { get; set; } 20 | 21 | [JsonProperty(JsonNames.Source)] 22 | public T Source { get; set; } 23 | 24 | [JsonIgnore] 25 | public string Highlight 26 | { 27 | get 28 | { 29 | if(HighlightList == null) 30 | { 31 | return null; 32 | } 33 | 34 | KeyValuePair firstHit = HighlightList.FirstOrDefault(h => h.Value != null && h.Value.Any()); 35 | 36 | if(firstHit.Value == null || !firstHit.Value.Any()) 37 | { 38 | return null; 39 | } 40 | 41 | return firstHit.Value[0]; 42 | } 43 | } 44 | 45 | [JsonProperty(JsonNames.Highlight)] 46 | private Dictionary HighlightList { get; set; } 47 | } 48 | 49 | internal class Hit : HitBase 50 | { 51 | } 52 | 53 | internal class CustomHit : HitBase 54 | { 55 | } 56 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/Hits.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Serialization 4 | { 5 | internal abstract class HitsBase 6 | { 7 | [JsonProperty(JsonNames.Hits)] 8 | public T[] HitArray { get; set; } 9 | 10 | [JsonProperty(JsonNames.Total)] 11 | public int Total { get; set; } 12 | 13 | [JsonProperty(JsonNames.MaxScore)] 14 | public double MaxScore { get; set; } 15 | } 16 | 17 | internal class Hits : HitsBase 18 | { 19 | } 20 | 21 | internal class CustomHits : HitsBase> 22 | { 23 | } 24 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/IndexStatus.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Serialization 4 | { 5 | internal class IndexStatus 6 | { 7 | public string Status { get; set; } 8 | 9 | [JsonProperty(JsonNames.TimedOut)] 10 | public bool TimedOut { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/Suggest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Serialization 4 | { 5 | internal class Suggest 6 | { 7 | [JsonProperty(JsonNames.DidYouMean)] 8 | public DidYouMeanHit[] DidYouMean { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/SuggestionOption.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Serialization 4 | { 5 | internal class SuggestionOption 6 | { 7 | [JsonProperty(JsonNames.Text)] 8 | public string Text { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/Suggestions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Serialization 4 | { 5 | internal class Suggestions 6 | { 7 | [JsonProperty(JsonNames.Options)] 8 | public SuggestionOption[] Options { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Serialization/SuggestionsRootObject.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Epinova.ElasticSearch.Core.Models.Serialization 4 | { 5 | internal class SuggestionsRootObject 6 | { 7 | [JsonProperty(JsonNames.Suggestions)] 8 | public Suggestions[] Suggestions { get; set; } 9 | 10 | [JsonProperty(JsonNames.Suggest)] 11 | public SuggestionsRootObject InnerRoot { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/Tracking.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models 2 | { 3 | public class Tracking 4 | { 5 | public string Query { get; set; } 6 | public long Searches { get; set; } 7 | public bool NoHits { get; set; } 8 | public string Language { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Models/TypeCount.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Models 2 | { 3 | public class TypeCount 4 | { 5 | public string ShortTypeName { get; set; } 6 | public string Name { get; set; } 7 | public string Group { get; set; } 8 | public string Type { get; set; } 9 | public int Count { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Pipelines/Attachment.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Pipelines 2 | { 3 | internal static class Attachment 4 | { 5 | internal const string Name = "attachment"; 6 | 7 | internal static readonly dynamic Definition = new 8 | { 9 | description = "Extract attachment information", 10 | processors = new[] 11 | { 12 | new 13 | { 14 | attachment = new 15 | { 16 | field = DefaultFields.AttachmentData, 17 | ignore_missing = true 18 | } 19 | } 20 | } 21 | }; 22 | } 23 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | [assembly: ComVisible(false)] 5 | [assembly: Guid("0818a108-083d-438b-9183-aef12733618f")] 6 | [assembly: InternalsVisibleTo("Epinova.ElasticSearch.Core.EPiServer")] 7 | [assembly: InternalsVisibleTo("Epinova.ElasticSearch.Core.EPiServer.Commerce")] 8 | [assembly: InternalsVisibleTo("Core.EPiServer.Tests")] 9 | [assembly: InternalsVisibleTo("Core.Tests")] 10 | [assembly: InternalsVisibleTo("TestData")] 11 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Settings/AssemblySettings.cs: -------------------------------------------------------------------------------- 1 | namespace Epinova.ElasticSearch.Core.Settings 2 | { 3 | internal static class AssemblySettings 4 | { 5 | internal static string[] IgnoreList => 6 | new[] { 7 | "antlr", 8 | "AutoFac", 9 | "AutoMapper", 10 | "BundleTransformer", 11 | "Castle", 12 | "DDay", 13 | "DynamicProxyGenAssembly2", 14 | "EntityFramework", 15 | "EPiServer", 16 | "FiftyOne", 17 | "ImageResizer", 18 | "ionic", 19 | "itextsharp", 20 | "JavaScriptEngineSwitcher", 21 | "jQuery", 22 | "log4net", 23 | "Mediachase", 24 | "Microsoft", 25 | "mscorlib", 26 | "MsieJavaScriptEngine", 27 | "NewRelic", 28 | "Newtonsoft", 29 | "Ninject", 30 | "NuGet", 31 | "System", 32 | "StructureMap", 33 | "WebDriver", 34 | "WebGrease" 35 | }; 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Settings/Configuration/FileConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Configuration; 2 | 3 | namespace Epinova.ElasticSearch.Core.Settings.Configuration 4 | { 5 | public class FileConfiguration : ConfigurationElement 6 | { 7 | internal const string InvalidCharacters = "~!#$%^&* ()[]{};'\"|\\:"; 8 | 9 | [ConfigurationProperty("extension", IsRequired = true, IsKey = true)] 10 | [StringValidator(InvalidCharacters = InvalidCharacters)] 11 | public string Extension 12 | { 13 | get => (string)this["extension"]; 14 | set => this["extension"] = value; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Settings/Configuration/IndexConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Configuration; 2 | 3 | namespace Epinova.ElasticSearch.Core.Settings.Configuration 4 | { 5 | public class IndexConfiguration : ConfigurationElement 6 | { 7 | internal const string NameInvalidCharacters = "~!#$%^&* ()[]{};'\"|\\.:"; 8 | 9 | [ConfigurationProperty("name", IsRequired = true, IsKey = true)] 10 | [StringValidator(InvalidCharacters = NameInvalidCharacters)] 11 | public string Name 12 | { 13 | get => (string)this["name"]; 14 | set => this["name"] = value; 15 | } 16 | 17 | [ConfigurationProperty("displayName", IsRequired = true)] 18 | public string DisplayName 19 | { 20 | get => (string)this["displayName"]; 21 | set => this["displayName"] = value; 22 | } 23 | 24 | [ConfigurationProperty("synonymsFile", IsRequired = false)] 25 | public string SynonymsFile 26 | { 27 | get => (string)this["synonymsFile"]; 28 | set => this["synonymsFile"] = value; 29 | } 30 | 31 | [ConfigurationProperty("default", DefaultValue = false, IsRequired = false)] 32 | public bool Default 33 | { 34 | get => (bool)this["default"]; 35 | set => this["default"] = value; 36 | } 37 | 38 | [ConfigurationProperty("type", IsRequired = false)] 39 | public string Type 40 | { 41 | get => (string)this["type"]; 42 | set => this["type"] = value; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Settings/Configuration/IndicesCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Configuration; 2 | 3 | namespace Epinova.ElasticSearch.Core.Settings.Configuration 4 | { 5 | public class IndicesCollection : ConfigurationElementCollection 6 | { 7 | public IndexConfiguration this[int index] 8 | { 9 | get => (IndexConfiguration)BaseGet(index); 10 | set 11 | { 12 | if(BaseGet(index) != null) 13 | { 14 | BaseRemoveAt(index); 15 | } 16 | BaseAdd(index, value); 17 | } 18 | } 19 | 20 | public void Add(IndexConfiguration serviceConfig) 21 | => BaseAdd(serviceConfig); 22 | 23 | public void Clear() 24 | => BaseClear(); 25 | 26 | protected override ConfigurationElement CreateNewElement() 27 | => new IndexConfiguration(); 28 | 29 | protected override object GetElementKey(ConfigurationElement element) 30 | => (IndexConfiguration)element; 31 | 32 | public void Remove(IndexConfiguration serviceConfig) 33 | => BaseRemove(serviceConfig); 34 | 35 | public void Remove(string name) 36 | => BaseRemove(name); 37 | 38 | public void RemoveAt(int index) 39 | => BaseRemoveAt(index); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Settings/IElasticSearchSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | 4 | namespace Epinova.ElasticSearch.Core.Settings 5 | { 6 | public interface IElasticSearchSettings 7 | { 8 | int BulkSize { get; } 9 | long DocumentMaxSize { get; } 10 | bool EnableFileIndexing { get; } 11 | string Host { get; } 12 | string Username { get; } 13 | string Password { get; } 14 | string Index { get; } 15 | IEnumerable Indices { get; } 16 | int ProviderMaxResults { get; } 17 | string GetCommerceIndexName(CultureInfo language); 18 | string GetCustomIndexName(string index, CultureInfo language); 19 | string GetDefaultIndexName(CultureInfo language); 20 | string GetLanguageFromIndexName(string indexName); 21 | string GetIndexNameWithoutLanguage(string indexName); 22 | int CloseIndexDelay { get; } 23 | bool IgnoreXhtmlStringContentFragments { get; } 24 | int ClientTimeoutSeconds { get; } 25 | int NumberOfShards { get; } 26 | int NumberOfReplicas { get; } 27 | bool CommerceEnabled { get; } 28 | bool UseTls12 { get; } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Utilities/Serialization.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | using Newtonsoft.Json; 4 | 5 | namespace Epinova.ElasticSearch.Core.Utilities 6 | { 7 | internal static class Serialization 8 | { 9 | private static readonly JsonSerializer Serializer = new JsonSerializer 10 | { 11 | ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 12 | NullValueHandling = NullValueHandling.Ignore 13 | }; 14 | 15 | internal static string Serialize(object body) 16 | { 17 | StringBuilder sb = new StringBuilder(); 18 | 19 | using(StringWriter tw = new StringWriter(sb)) 20 | { 21 | Serializer.Serialize(tw, body); 22 | } 23 | 24 | return sb.ToString(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Utilities/TextUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using EPiServer.Core.Html; 4 | 5 | namespace Epinova.ElasticSearch.Core.Utilities 6 | { 7 | internal static class TextUtil 8 | { 9 | private static readonly Regex Pattern = new Regex(@"&[^\s;]+;", RegexOptions.Compiled | RegexOptions.IgnoreCase); 10 | 11 | internal static bool IsNumeric(string input) 12 | => input != null && Int64.TryParse(input, out _); 13 | 14 | internal static string StripHtmlAndEntities(string input) 15 | => Pattern.Replace(StripHtml(input), " "); 16 | 17 | internal static string StripHtml(string input) 18 | { 19 | if(input == null) 20 | { 21 | return String.Empty; 22 | } 23 | 24 | return TextIndexer.StripHtml(input, Int32.MaxValue) ?? String.Empty; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/Utilities/Validation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Epinova.ElasticSearch.Core.Utilities 4 | { 5 | internal static class Validation 6 | { 7 | internal static void EnsureNotNull(this string instance, string message = null) 8 | { 9 | if(instance == null) 10 | { 11 | throw new ArgumentNullException(message); 12 | } 13 | } 14 | 15 | internal static void EnsureNotNull(this object instance, string message = null) 16 | { 17 | if(instance == null) 18 | { 19 | throw new ArgumentNullException(message); 20 | } 21 | } 22 | 23 | private static void EnsureNotNull(this string[] instance, string message = null) 24 | { 25 | if(instance == null) 26 | { 27 | throw new ArgumentNullException(message); 28 | } 29 | } 30 | 31 | internal static void EnsureNotNullOrEmpty(this string[] instance, string message = null) 32 | { 33 | instance.EnsureNotNull(); 34 | 35 | if(instance.Length == 0) 36 | { 37 | throw new ArgumentNullException(message); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Epinova.ElasticSearch.Core/WellKnownProperties.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Epinova.ElasticSearch.Core 4 | { 5 | internal static class WellKnownProperties 6 | { 7 | internal static readonly string[] AutoAnalyze = 8 | { 9 | DefaultFields.AttachmentContent, 10 | "MainIntro", 11 | "MainBody", 12 | "Description" 13 | }; 14 | 15 | internal static readonly List Analyze = new List(AutoAnalyze); 16 | 17 | internal static readonly string[] IgnoreAnalyzer = 18 | { 19 | DefaultFields.Id, 20 | DefaultFields.BestBets, 21 | DefaultFields.ParentId, 22 | DefaultFields.Path, 23 | DefaultFields.Name, 24 | DefaultFields.Lang, 25 | DefaultFields.DidYouMean, 26 | DefaultFields.Suggest, 27 | DefaultFields.Type, 28 | DefaultFields.Types, 29 | DefaultFields.AttachmentData, 30 | }; 31 | 32 | internal static readonly string[] Ignore = 33 | { 34 | "CreatedBy", 35 | "ChangedBy", 36 | "ContentLink", 37 | "DeletedBy", 38 | "URLSegment", 39 | "ExternalURL", 40 | "PageName", 41 | "ParentLink", 42 | "RouteSegment", 43 | "LinkURL", 44 | "StaticLinkURL", 45 | "LanguageID", 46 | "LanguageBranch", 47 | "TargetFrameName", 48 | "MasterLanguageBranch" 49 | }; 50 | } 51 | } -------------------------------------------------------------------------------- /tests/Core.Episerver.Tests/Models/ViewModels/Abstractions/LanguageAwareViewModelBaseTests.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.EPiServer.Models.ViewModels.Abstractions; 2 | using Xunit; 3 | 4 | namespace Core.Episerver.Tests.Models.ViewModels.Abstractions 5 | { 6 | public class LanguageAwareViewModelBaseTests 7 | { 8 | private class TestableLanguageAwareViewModelBase : LanguageAwareViewModelBase 9 | { 10 | public TestableLanguageAwareViewModelBase(string currentLanguage) : base(currentLanguage) 11 | { 12 | } 13 | } 14 | 15 | [Fact] 16 | public void Ctor_CurrentLanguageIsNull_ReturnsEmptyString() 17 | { 18 | var model = new TestableLanguageAwareViewModelBase(null); 19 | Assert.Empty(model.CurrentLanguage); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /tests/Core.Episerver.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using Xunit; 4 | 5 | [assembly: ComVisible(false)] 6 | 7 | [assembly: Guid("8c759859-cfab-4069-990e-867e3c6bd453")] 8 | [assembly: CollectionBehavior(DisableTestParallelization = true, MaxParallelThreads = 1)] -------------------------------------------------------------------------------- /tests/Core.Episerver.Tests/ServiceLocatiorCollection.cs: -------------------------------------------------------------------------------- 1 | using TestData; 2 | using Xunit; 3 | 4 | namespace Core.Episerver.Tests 5 | { 6 | [CollectionDefinition(nameof(ServiceLocatiorCollection))] 7 | public class ServiceLocatiorCollection : ICollectionFixture 8 | { 9 | // This class has no code, and is never created. Its purpose is simply 10 | // to be the place to apply [CollectionDefinition] and all the 11 | // ICollectionFixture<> interfaces. 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/Core.Tests/Conventions/IndexingTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Epinova.ElasticSearch.Core.Conventions; 3 | using TestData; 4 | using Xunit; 5 | 6 | namespace Core.Tests.Conventions 7 | { 8 | [Collection(nameof(ServiceLocatiorCollection))] 9 | public class IndexingTests 10 | { 11 | public IndexingTests() 12 | { 13 | Indexing.Extensions.Clear(); 14 | } 15 | 16 | [Theory] 17 | [InlineData("")] 18 | [InlineData(" ")] 19 | [InlineData(null)] 20 | public void IncludeFileType_NullOrEmptyString_DoesNotAddToCollection(string type) 21 | { 22 | Indexing.Instance.IncludeFileType(type); 23 | 24 | var result = Indexing.IncludedFileExtensions.Length; 25 | 26 | Assert.Equal(0, result); 27 | } 28 | 29 | [Fact] 30 | public void IncludeFileType_ValidString_AddsToCollection() 31 | { 32 | Indexing.Instance.IncludeFileType("pdf"); 33 | 34 | var result = Indexing.IncludedFileExtensions.Length; 35 | 36 | Assert.True(result > 0); 37 | } 38 | 39 | [Fact] 40 | public void ExcludeType_AnyType_AddsToCollection() 41 | { 42 | Indexing.Instance.ExcludeType(); 43 | 44 | Type[] result = Indexing.ExcludedTypes; 45 | 46 | Assert.Contains(typeof(string), result); 47 | } 48 | 49 | [Fact] 50 | public void ExcludeRoot_AddsToCollection() 51 | { 52 | var rootId = Factory.GetInteger(); 53 | Indexing.Instance.ExcludeRoot(rootId); 54 | 55 | var result = Indexing.ExcludedRoots; 56 | 57 | Assert.Contains(rootId, result); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/Core.Tests/Conventions/MessageHandlerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using Epinova.ElasticSearch.Core.Conventions; 3 | using Xunit; 4 | 5 | namespace Core.Tests.Conventions 6 | { 7 | [Collection(nameof(ServiceLocatiorCollection))] 8 | public class MessageHandlerTests 9 | { 10 | [Fact] 11 | public void SetMessageHandler_IsCorrectType() 12 | { 13 | MessageHandler.Instance.SetMessageHandler(new MockHandler()); 14 | 15 | HttpMessageHandler result = MessageHandler.Instance.Handler; 16 | 17 | Assert.IsType(result); 18 | } 19 | 20 | private class MockHandler : DelegatingHandler 21 | { 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/Core.Tests/Core.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net472 4 | Core.Tests 5 | Core.Tests 6 | Copyright© 2022 - Epinova AS 7 | false 8 | full 9 | true 10 | CS1591 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | all 20 | 21 | 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | all 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /tests/Core.Tests/Extensions/SimpleSearchExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Enums; 2 | using Epinova.ElasticSearch.Core.Extensions; 3 | using Xunit; 4 | 5 | namespace Core.Tests.Extensions 6 | { 7 | public class SimpleSearchExtensionsTests 8 | { 9 | [Fact] 10 | public void AsJsonValue_Returns_Values() 11 | { 12 | SimpleQuerystringOperators value = SimpleQuerystringOperators.Not | SimpleQuerystringOperators.Or | SimpleQuerystringOperators.Phrase; 13 | Assert.Equal("NOT|OR|PHRASE", value.AsJsonValue()); 14 | } 15 | 16 | [Fact] 17 | public void AsJsonValue_Returns_None_As_Single_Value() 18 | { 19 | Assert.Equal("NONE", SimpleQuerystringOperators.None.AsJsonValue()); 20 | } 21 | 22 | [Fact] 23 | public void AsJsonValue_Returns_All_As_Single_Value() 24 | { 25 | Assert.Equal("ALL", SimpleQuerystringOperators.All.AsJsonValue()); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Core.Tests/Models/Converters/GeoPointConverterTests.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Models.Properties; 2 | using Newtonsoft.Json; 3 | using Xunit; 4 | 5 | namespace Core.Tests.Models.Converters 6 | { 7 | public class GeoPointConverterTests 8 | { 9 | [Fact] 10 | public void ReadJson_CoordsString_Converts() 11 | { 12 | var json = "\"35.6681625,139.6007856\""; 13 | 14 | var result = JsonConvert.DeserializeObject(json); 15 | 16 | Assert.Equal(35.6681625, result.Lat); 17 | Assert.Equal(139.6007856, result.Lon); 18 | } 19 | 20 | [Theory] 21 | [InlineData(null)] 22 | [InlineData("")] 23 | [InlineData("foo")] 24 | [InlineData("42")] 25 | public void ReadJson_InvalidString_ReturnsNull(string input) 26 | { 27 | var json = "\"" + input + "\""; 28 | 29 | var result = JsonConvert.DeserializeObject(json); 30 | 31 | Assert.Null(result); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /tests/Core.Tests/Models/Properties/GeoPointTests.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Models.Properties; 2 | using Xunit; 3 | 4 | namespace Core.Tests.Models.Properties 5 | { 6 | public class GeoPointTests 7 | { 8 | [Theory] 9 | [InlineData("asd")] 10 | [InlineData(" ")] 11 | [InlineData(null)] 12 | [InlineData("-123.456,-234.567")] 13 | public void Parse_InvalidInput_ReturnsNull(string input) 14 | { 15 | var result = GeoPoint.Parse(input); 16 | Assert.Null(result); 17 | } 18 | 19 | [Theory] 20 | [InlineData("10,10")] 21 | [InlineData("-10,-10")] 22 | [InlineData("59.9152868,10.7519013")] 23 | [InlineData("-34.6158037,-58.5033379")] 24 | [InlineData("35.6681625,139.6007856")] 25 | public void Parse_ValidInput_ReturnsInstance(string input) 26 | { 27 | var result = GeoPoint.Parse(input); 28 | Assert.NotNull(result); 29 | } 30 | 31 | [Fact] 32 | public void ToString_UsesInvariantCommaSeparator() 33 | { 34 | var coords = "35.6681625,139.6007856"; 35 | var instance = GeoPoint.Parse(coords); 36 | var result = instance.ToString(); 37 | Assert.Equal(coords, result); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /tests/Core.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using Xunit; 4 | [assembly: ComVisible(false)] 5 | [assembly: Guid("e5af7b5d-6c93-44af-91b5-3d2e85e56203")] 6 | [assembly: CollectionBehavior(DisableTestParallelization = true, MaxParallelThreads = 1)] -------------------------------------------------------------------------------- /tests/Core.Tests/ServiceLocatiorCollection.cs: -------------------------------------------------------------------------------- 1 | using TestData; 2 | using Xunit; 3 | 4 | namespace Core.Tests 5 | { 6 | [CollectionDefinition(nameof(ServiceLocatiorCollection))] 7 | public class ServiceLocatiorCollection : ICollectionFixture 8 | { 9 | // This class has no code, and is never created. Its purpose is simply 10 | // to be the place to apply [CollectionDefinition] and all the 11 | // ICollectionFixture<> interfaces. 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/Core.Tests/Utilities/ArrayHelperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | using Epinova.ElasticSearch.Core.Utilities; 5 | using TestData; 6 | using Xunit; 7 | 8 | namespace Core.Tests.Utilities 9 | { 10 | public class ArrayHelperTests 11 | { 12 | [Theory] 13 | [InlineData(typeof(int))] 14 | [InlineData(typeof(TestPage))] 15 | [InlineData(typeof(DateTime))] 16 | [InlineData(typeof(char))] 17 | public void ToArray_NonEnumerableType_ReturnsEmptyArray(Type instance) 18 | { 19 | var result = ArrayHelper.ToArray(instance); 20 | 21 | Assert.Equal(Enumerable.Empty(), result); 22 | } 23 | 24 | [Theory] 25 | [InlineData("foo")] 26 | [InlineData(new[] { 1, 2, 3 })] 27 | [InlineData(new[] { '1', '2', '3' })] 28 | [InlineData(new[] { Double.MaxValue, Double.MinValue, Double.MaxValue })] 29 | public void ToArray_EnumerableType_ReturnsArray(object instance) 30 | { 31 | var result = (IEnumerable)ArrayHelper.ToArray(instance); 32 | 33 | Assert.NotEmpty(result); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /tests/Core.Tests/Utilities/ValidationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Epinova.ElasticSearch.Core.Utilities; 3 | using Xunit; 4 | 5 | namespace Core.Tests.Utilities 6 | { 7 | public class ValidationTests 8 | { 9 | [Fact] 10 | public void EnsureNotNull_NullValue_Throws() 11 | { 12 | const string instance = null; 13 | 14 | Assert.Throws(() => instance.EnsureNotNull()); 15 | } 16 | 17 | [Fact] 18 | public void EnsureNotNull_ValidString_DoesNotThrow() 19 | { 20 | const string instance = "a"; 21 | 22 | instance.EnsureNotNull(); 23 | } 24 | 25 | [Fact] 26 | public void EnsureNotNullOrEmpty_NullValue_Throws() 27 | { 28 | string[] instance = null; 29 | 30 | Assert.Throws(() => instance.EnsureNotNullOrEmpty()); 31 | } 32 | 33 | [Fact] 34 | public void EnsureNotNullOrEmpty_EmptyArray_Throws() 35 | { 36 | var instance = new string[0]; 37 | 38 | Assert.Throws(() => instance.EnsureNotNullOrEmpty()); 39 | } 40 | 41 | [Fact] 42 | public void EnsureNotNullOrEmpty_ValidArray_DoesNotThrow() 43 | { 44 | string[] instance = { "a" }; 45 | 46 | instance.EnsureNotNullOrEmpty(); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /tests/TestData/ComplexType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Epinova.ElasticSearch.Core.Models.Properties; 3 | using EPiServer.DataAnnotations; 4 | 5 | namespace TestData 6 | { 7 | public class ComplexType 8 | { 9 | public ComplexType() 10 | { 11 | StringProperty = Factory.GetString(); 12 | IntProperty = Factory.GetInteger(); 13 | Id = Factory.GetInteger(); 14 | DateTimeProperty = DateTime.Now; 15 | } 16 | 17 | public int Id { get; set; } 18 | 19 | [Searchable] 20 | public string StringProperty { get; set; } 21 | 22 | public int IntProperty { get; set; } 23 | 24 | public long LongProperty { get; set; } 25 | 26 | public double DoubleProperty { get; set; } 27 | 28 | public float FloatProperty { get; set; } 29 | 30 | public decimal DecimalProperty { get; set; } 31 | 32 | public DateTime DateTimeProperty { get; set; } 33 | 34 | public bool BoolProperty { get; set; } 35 | 36 | public GeoPoint GeoPointProperty { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/TestData/ElasticFixtureSettings.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core; 2 | 3 | namespace TestData 4 | { 5 | public static class ElasticFixtureSettings 6 | { 7 | public static readonly string IndexName = $"my-index{Constants.IndexNameLanguageSplitChar}no"; 8 | public static string IndexNameWithoutLang = "my-index"; 9 | } 10 | } -------------------------------------------------------------------------------- /tests/TestData/FakeIFileUploadElementBlock.cs: -------------------------------------------------------------------------------- 1 | namespace EPiServer.Forms.Core 2 | { 3 | internal interface IFileUploadElementBlock 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/TestData/Inheritance.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | 3 | namespace TestData 4 | { 5 | public class TestClassA : TestClassB, ITestInterfaceA 6 | { 7 | } 8 | 9 | public class TestClassB : TestClassC 10 | { 11 | } 12 | 13 | public class TestClassC : TestClassD 14 | { 15 | } 16 | 17 | public class TestClassD : BasicContent 18 | { 19 | public TestClassD() 20 | { 21 | Property = new PropertyDataCollection(); 22 | } 23 | } 24 | 25 | public interface ITestInterfaceA 26 | { 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/TestData/Json/Facets_Field_Foo.json: -------------------------------------------------------------------------------- 1 | { 2 | "size": 0, 3 | "aggregations": { 4 | "aggregation": { 5 | "terms": { 6 | "field": "Foo" 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /tests/TestData/Json/Facets_Field_Type.json: -------------------------------------------------------------------------------- 1 | { 2 | "size": 0, 3 | "aggregations": { 4 | "aggregation": { 5 | "terms": { 6 | "field": "Type" 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /tests/TestData/Json/HealthInfo.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "epoch": "1561477363", 4 | "timestamp": "17:42:43", 5 | "cluster": "elasticsearch", 6 | "status": "yellow", 7 | "node.total": "1", 8 | "node.data": "1", 9 | "shards": "260", 10 | "pri": "260", 11 | "relo": "0", 12 | "init": "0", 13 | "unassign": "260", 14 | "pending_tasks": "0", 15 | "max_task_wait_time": "-", 16 | "active_shards_percent": "50.0%" 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /tests/TestData/Json/IndicesInfo.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "health": "yellow", 4 | "status": "open", 5 | "index": "my-index¤no", 6 | "uuid": "6UZZZes7SjWllPPL554clg", 7 | "pri": "5", 8 | "rep": "1", 9 | "docs.count": "123", 10 | "docs.deleted": "0", 11 | "store.size": "22.6kb", 12 | "pri.store.size": "22.6kb" 13 | } 14 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/NodeInfo.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "m": "*", 4 | "v": "5.6.10", 5 | "http": "127.0.0.1:9200", 6 | "d": "267.8gb", 7 | "rc": "21.1gb", 8 | "rm": "63.9gb", 9 | "u": "8.5h", 10 | "n": "DESKTOP-C6VLCP6" 11 | } 12 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldBool_false.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": false 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldBool_true.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": true 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldDate.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": "1980-01-30T00:00:00" 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldDateTime.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": "1980-01-30T23:59:59" 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldDouble_-123456.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": -123456.0 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldDouble_123456-7.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": 123456.7 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldDouble_123456.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": 123456.0 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldFloat_-123456.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": -123456.0 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldFloat_0.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": 0.0 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldFloat_123456.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": 123456.0 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldInt_-2147483648.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": -2147483648 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldInt_0.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": 0 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldInt_1234.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": 1234 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldInt_2147483647.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": 2147483647 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldLong_-9223372036854775808.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": -9223372036854775808 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldLong_0.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": 0 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldLong_1234.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": 1234 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldLong_9223372036854775807.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": 9223372036854775807 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldString_Foo.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": "Foo" 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/PostFilterShouldString_LoremIpsumBaconOmgLongStringMkay.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "term": { 4 | "MyField": "LoremIpsumBaconOmgLongStringMkay" 5 | } 6 | } 7 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/Results_No_Hits.json: -------------------------------------------------------------------------------- 1 | { 2 | "took": 1, 3 | "timed_out": false, 4 | "_shards": { 5 | "total": 5, 6 | "successful": 5, 7 | "failed": 0 8 | }, 9 | "hits": { 10 | "total": 0, 11 | "max_score": null, 12 | "hits": [] 13 | } 14 | } -------------------------------------------------------------------------------- /tests/TestData/Json/Results_With_Custom_Properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "hits": { 3 | "total": 1, 4 | "max_score": 1.1784737, 5 | "hits": [ 6 | { 7 | "_index": "nitodemo", 8 | "_type": "Epinova.ElasticSearch.Core.EPiServer.IndexItem", 9 | "_id": "46", 10 | "_score": 1.1784737, 11 | "_source": { 12 | "Id": 46, 13 | "Title": "Nyhet test bacon", 14 | "Updated": "2015-03-31T23:01:04.2493062+02:00", 15 | "Type": "TestData_TestPage", 16 | "Types": [ 17 | "TestData_TestPage", 18 | "EPiServer_Core_ContentData", 19 | "System_Object" 20 | ], 21 | "Name": "Nyhet test bacon", 22 | "Date": "2015-03-31T23:01:04.2493062+02:00", 23 | "Text": "Lorem text", 24 | "Integer": 327, 25 | "Decimal": 42.1, 26 | "Double": 42.1, 27 | "Long": 4221344, 28 | "Array1": [ 1, 2, 3 ], 29 | "Array2": [ "Foo", "Bar" ], 30 | "DictionaryString": { 31 | "key1": "value1", 32 | "key2": "1" 33 | }, 34 | "DictionaryInt": { 35 | "key2": 1, 36 | "key3": 2 37 | }, 38 | "DictionaryStringArray": { 39 | "key1": [ "1", "2" ], 40 | "key2": [ "3", "4" ] 41 | }, 42 | "DictionaryIntArray": { 43 | "key1": [ 1, 2 ], 44 | "key2": [ 3, 4 ] 45 | } 46 | } 47 | } 48 | ] 49 | } 50 | } -------------------------------------------------------------------------------- /tests/TestData/Json/Results_With_Custom_Properties_NullValues.json: -------------------------------------------------------------------------------- 1 | { 2 | "hits": { 3 | "total": 1, 4 | "max_score": 1.1784737, 5 | "hits": [ 6 | { 7 | "_index": "nitodemo", 8 | "_type": "Epinova.ElasticSearch.Core.EPiServer.IndexItem", 9 | "_id": "46", 10 | "_score": 1.1784737, 11 | "_source": { 12 | "Id": 46, 13 | "Title": "Nyhet test bacon", 14 | "Updated": "2015-03-31T23:01:04.2493062+02:00", 15 | "Type": "TestData_TestPage", 16 | "Types": [ 17 | "TestData_TestPage", 18 | "EPiServer_Core_ContentData", 19 | "System_Object" 20 | ], 21 | "Name": "Nyhet test bacon", 22 | "Date": null, 23 | "Text": null, 24 | "Int": null, 25 | "Dec": null, 26 | "Array1": null, 27 | "Array2": null 28 | } 29 | } 30 | ] 31 | } 32 | } -------------------------------------------------------------------------------- /tests/TestData/Json/SearchOfT_Object.json: -------------------------------------------------------------------------------- 1 | "must": [ 2 | { 3 | "multi_match": { 4 | "query": "term", 5 | "lenient": true, 6 | "operator": "or", 7 | "fields": [ 8 | "bar", 9 | "attachment.content", 10 | "attachment.author", 11 | "attachment.keywords" 12 | ] 13 | } 14 | } 15 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/SearchOfT_String.json: -------------------------------------------------------------------------------- 1 | "must": [ 2 | { 3 | "multi_match": { 4 | "query": "term", 5 | "lenient": true, 6 | "operator": "or", 7 | "fields": [ 8 | "bar", 9 | "attachment.content", 10 | "attachment.author", 11 | "attachment.keywords" 12 | ] 13 | } 14 | } 15 | ] -------------------------------------------------------------------------------- /tests/TestData/Json/Search_Term_Foo-Bar.json: -------------------------------------------------------------------------------- 1 | "must": [ 2 | { 3 | "multi_match": { 4 | "query": "foo bar", 5 | "lenient": true, 6 | "operator": "or", 7 | "fields": [ 8 | "bar", 9 | "attachment.content", 10 | "attachment.author", 11 | "attachment.keywords" 12 | ] 13 | } 14 | } 15 | ], 16 | "should": [ 17 | { 18 | "multi_match": { 19 | "query": "foo bar", 20 | "lenient": true, 21 | "operator": "or", 22 | "type": "phrase", 23 | "boost": 2, 24 | "fields": [ 25 | "bar", 26 | "attachment.content", 27 | "attachment.author", 28 | "attachment.keywords" 29 | ] 30 | } 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /tests/TestData/Json/Search_Term_Foo.json: -------------------------------------------------------------------------------- 1 | "must": [ 2 | { 3 | "multi_match": { 4 | "query": "foo", 5 | "lenient": true, 6 | "operator": "or", 7 | "fields": [ 8 | "bar", 9 | "attachment.content", 10 | "attachment.author", 11 | "attachment.keywords" 12 | ] 13 | } 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /tests/TestData/Json/Search_With_Filter_123_Term_Foo.json: -------------------------------------------------------------------------------- 1 | "filter":[{"bool":{"must":[{"term":{"Path":"123"}}]}}] -------------------------------------------------------------------------------- /tests/TestData/Json/Search_With_Filter_123_Term_Foo_Bar.json: -------------------------------------------------------------------------------- 1 | "filter":[{"bool":{"must":[{"term":{"Path":"123"}}]}}] -------------------------------------------------------------------------------- /tests/TestData/Json/Search_With_Filter_678_Term_Bar.json: -------------------------------------------------------------------------------- 1 | "filter":[{"bool":{"must":[{"term":{"Path":"678"}}]}}] -------------------------------------------------------------------------------- /tests/TestData/Json/SimpleQuerystring_Term_Foo.json: -------------------------------------------------------------------------------- 1 | "must": [ 2 | { 3 | "simple_query_string": { 4 | "query": "foo", 5 | "lenient": true, 6 | "default_operator": "or", 7 | "flags": "ALL", 8 | "fields": [ 9 | "bar", 10 | "attachment.content", 11 | "attachment.author", 12 | "attachment.keywords" 13 | ] 14 | } 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /tests/TestData/Json/SimpleQuerystring_Term_Foo_Bar_And_Near_Or.json: -------------------------------------------------------------------------------- 1 | "must": [ 2 | { 3 | "simple_query_string": { 4 | "query": "foo bar", 5 | "lenient": true, 6 | "default_operator": "or", 7 | "flags": "AND|NEAR|OR", 8 | "fields": [ 9 | "bar", 10 | "attachment.content", 11 | "attachment.author", 12 | "attachment.keywords" 13 | ] 14 | } 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /tests/TestData/Json/Suggestions_2hits.json: -------------------------------------------------------------------------------- 1 | { 2 | "_shards": { 3 | "total": 1, 4 | "successful": 1, 5 | "failed": 0 6 | }, 7 | "suggestions": [ 8 | { 9 | "text": "word", 10 | "offset": 0, 11 | "length": 4, 12 | "options": [ 13 | { 14 | "text": "wordland", 15 | "score": 1 16 | }, 17 | { 18 | "text": "wordpress", 19 | "score": 1 20 | } 21 | ] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /tests/TestData/Media/HelloWorld..exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/tests/TestData/Media/HelloWorld..exe -------------------------------------------------------------------------------- /tests/TestData/Media/HelloWorld.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/tests/TestData/Media/HelloWorld.docx -------------------------------------------------------------------------------- /tests/TestData/Media/HelloWorld.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Epinova/Epinova.Elasticsearch/750ceedab59ace47b407cd0ea5e37cb68df43ae1/tests/TestData/Media/HelloWorld.pdf -------------------------------------------------------------------------------- /tests/TestData/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: ComVisible(false)] 6 | 7 | [assembly: Guid("a753a2ce-68de-48ac-ab1e-8635cabb181c")] 8 | 9 | [assembly: InternalsVisibleTo("Core.Tests")] 10 | [assembly: InternalsVisibleTo("Core.Episerver.Tests")] -------------------------------------------------------------------------------- /tests/TestData/ServiceLocationMock.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Contracts; 2 | using Epinova.ElasticSearch.Core.EPiServer.Contracts; 3 | using Epinova.ElasticSearch.Core.Settings; 4 | using EPiServer; 5 | using EPiServer.Core; 6 | using EPiServer.DataAbstraction; 7 | using EPiServer.Scheduler; 8 | using EPiServer.ServiceLocation; 9 | using EPiServer.Web; 10 | using Moq; 11 | 12 | namespace TestData 13 | { 14 | public class ServiceLocationMock 15 | { 16 | public Mock ServiceLocatorMock { get; set; } 17 | public Mock IndexerMock { get; set; } 18 | public Mock CoreIndexerMock { get; set; } 19 | public Mock SynonymRepositoryMock { get; set; } 20 | public Mock ContentLoaderMock { get; set; } 21 | public Mock ContentIndexServiceMock { get; set; } 22 | public Mock SettingsMock { get; set; } 23 | public Mock ServerInfoMock { get; set; } 24 | public Mock HttpClientMock { get; set; } 25 | public Mock SearchLanguageMock { get; set; } 26 | public Mock> ServiceMock { get; set; } 27 | public Mock LanguageBranchRepositoryMock { get; set; } 28 | public Mock StateAssesorMock { get; set; } 29 | public Mock ScheduledJobRepositoryMock { get; set; } 30 | public Mock ScheduledJobExecutorMock { get; set; } 31 | public ITemplateResolver TemplateResolver { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/TestData/ServiceLocatorFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Epinova.ElasticSearch.Core; 3 | using EPiServer.ServiceLocation; 4 | 5 | namespace TestData 6 | { 7 | public sealed class ServiceLocatorFixture : IDisposable 8 | { 9 | public ServiceLocatorFixture() 10 | { 11 | ServiceLocationMock = Factory.SetupServiceLocator(); 12 | } 13 | 14 | public void Dispose() => ServiceLocator.SetLocator(null); 15 | 16 | public ServiceLocationMock ServiceLocationMock { get; } 17 | 18 | public void MockInfoEndpoints() 19 | { 20 | ServiceLocationMock.HttpClientMock 21 | .Setup(m => m.GetJson(new Uri("http://example.com/_cat/indices?format=json"))) 22 | .Returns(Factory.GetJsonTestData("IndicesInfo.json")); 23 | ServiceLocationMock.HttpClientMock 24 | .Setup(m => m.GetJson(new Uri("http://example.com/_cat/health?format=json"))) 25 | .Returns(Factory.GetJsonTestData("HealthInfo.json")); 26 | ServiceLocationMock.HttpClientMock 27 | .Setup(m => m.GetJson(new Uri("http://example.com/_cat/nodes?format=json&h=m,v,http,d,rc,rm,u,n"))) 28 | .Returns(Factory.GetJsonTestData("NodeInfo.json")); 29 | ServiceLocationMock.HttpClientMock 30 | .Setup(m => m.GetString(new Uri($"http://example.com/my-index{Constants.IndexNameLanguageSplitChar}no/_settings"))) 31 | .Returns(Factory.GetJsonTestData("Settings.json")); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/TestData/TestBlock.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | 3 | namespace TestData 4 | { 5 | public class TestBlock : BlockData 6 | { 7 | public virtual string TestProp { get; set; } 8 | 9 | public virtual TestBlockInherited SubBlock { get; set; } 10 | } 11 | 12 | public class TestBlockInherited : TestBlock 13 | { 14 | public virtual string TestProp2 { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /tests/TestData/TestData.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net472 4 | TestData 5 | TestData 6 | Copyright© 2022 - Epinova AS 7 | false 8 | full 9 | true 10 | false 11 | CS1591 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/TestData/TestMedia.cs: -------------------------------------------------------------------------------- 1 | using EPiServer.Core; 2 | using EPiServer.Framework.DataAnnotations; 3 | 4 | namespace TestData 5 | { 6 | [MediaDescriptor(ExtensionString = "docx,pdf")] 7 | public class TestMedia : MediaData 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/TestData/TestableContentFragment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using EPiServer; 4 | using EPiServer.Core; 5 | using EPiServer.Core.Html.StringParsing; 6 | using EPiServer.Security; 7 | using EPiServer.Web; 8 | using Moq; 9 | 10 | namespace TestData 11 | { 12 | public class TestableContentFragment : ContentFragment 13 | { 14 | private readonly IContent _content; 15 | 16 | public TestableContentFragment() : this(null) 17 | { 18 | } 19 | 20 | public TestableContentFragment(IContent content) 21 | : base(new Mock().Object, 22 | new Mock().Object, 23 | new Mock().Object, 24 | new Mock().Object, 25 | new Mock().Object, 26 | new Mock().Object, 27 | new Mock>().Object) 28 | { 29 | _content = content; 30 | ContentLink = content.ContentLink; 31 | } 32 | 33 | public override IContent GetContent() => _content; 34 | 35 | public override IContent GetContent(bool enableMasterLanguageFallback) => _content; 36 | 37 | [Obsolete("Use GetContent() instead.", false)] 38 | public override IContent Content => _content; 39 | 40 | [Obsolete("Use GetContent() instead.", false)] 41 | public override IContentData ContentData => _content; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/TestData/TestableSearchEngine.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.IO; 3 | using Epinova.ElasticSearch.Core.Contracts; 4 | using Epinova.ElasticSearch.Core.Engine; 5 | using Epinova.ElasticSearch.Core.Models; 6 | using Epinova.ElasticSearch.Core.Models.Query; 7 | using Epinova.ElasticSearch.Core.Settings; 8 | using Newtonsoft.Json; 9 | 10 | namespace TestData 11 | { 12 | internal class TestableSearchEngine : SearchEngine 13 | { 14 | private readonly string[] _suggestions; 15 | private readonly string _jsonFile; 16 | 17 | public TestableSearchEngine(string jsonFile, IServerInfoService serverInfoService, IElasticSearchSettings settings, IHttpClientHelper httpClientHelper) 18 | : base(serverInfoService, settings, httpClientHelper) 19 | { 20 | _jsonFile = jsonFile; 21 | } 22 | 23 | public TestableSearchEngine(string[] suggestions, IServerInfoService serverInfoService, IElasticSearchSettings settings, IHttpClientHelper httpClientHelper) 24 | : base(serverInfoService, settings, httpClientHelper) 25 | { 26 | _suggestions = suggestions; 27 | } 28 | 29 | public override string[] GetSuggestions(SuggestRequest request, CultureInfo culture, string indexName = null) => _suggestions ?? base.GetSuggestions(request, culture, indexName); 30 | 31 | public override JsonReader GetResponse(RequestBase request, string endpoint, out string rawJsonResult) 32 | { 33 | rawJsonResult = null; 34 | return new JsonTextReader(new StringReader(Factory.GetJsonTestData(_jsonFile))); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /tests/TestData/TypeWithBoosting.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Attributes; 2 | 3 | namespace TestData 4 | { 5 | public class TypeWithBoosting 6 | { 7 | [Boost] 8 | public string BoostedProperty { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/TestData/TypeWithExcludeAttribute.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Attributes; 2 | using EPiServer.Core; 3 | 4 | namespace TestData 5 | { 6 | [ExcludeFromSearch] 7 | public class TypeWithExcludeAttribute : BasicContent 8 | { 9 | public TypeWithExcludeAttribute() 10 | { 11 | ContentLink = Factory.GetPageReference(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/TestData/TypeWithHideFromSearchProperty.cs: -------------------------------------------------------------------------------- 1 | using Epinova.ElasticSearch.Core.Attributes; 2 | using EPiServer.Core; 3 | 4 | namespace TestData 5 | { 6 | [ExcludeFromSearch] 7 | public sealed class TypeWithHideFromSearchProperty : BasicContent 8 | { 9 | public TypeWithHideFromSearchProperty() 10 | { 11 | Property["HideFromSearch"] = new PropertyBoolean(true); 12 | ContentLink = Factory.GetPageReference(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/TestData/TypeWithoutBoosting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EPiServer.Core; 3 | 4 | namespace TestData 5 | { 6 | public class TypeWithoutBoosting : ContentData, IContent 7 | { 8 | public string NormalProperty { get; set; } 9 | public string Name { get; set; } 10 | public ContentReference ContentLink { get; set; } 11 | public ContentReference ParentLink { get; set; } 12 | public Guid ContentGuid { get; set; } 13 | public int ContentTypeID { get; set; } 14 | public bool IsDeleted { get; set; } 15 | } 16 | } 17 | --------------------------------------------------------------------------------