Repository: owncloud/ocis
Pull request #4305 (2022-07-30 18:56:09)

[full-ci] Search content extraction 
## Description
Search engine now is able to extract content from uploaded documents, to do so, a new content package is introduced which utilizes [apache tika](https://tika.apache.org/) to do the actual content extraction. It also takes care to remove stopwords lik is, and, can.....

Beside the fact of content extraction now is possible, it also restructures the search engine to be able to inject different search engine proiders, bleve for the moment is still the only engine, search service is shipped with.

following config options have been added:
* `SEARCH_EVENTS_ASYNC_UPLOADS`, use the new async uploads feature to extract the content
* `SEARCH_ENGINE_TYPE`, define which search engine to use, only bleve is supported at the moment of writing
* `SEARCH_DATA_PATH` renamed to `SEARCH_ENGINE_BLEVE_DATA_PATH`
* `SEARCH_EXTRACTOR_TYPE`, possible options are `tika` and `basic`, basic is the same as before
* `SEARCH_EXTRACTOR_CS3SOURCE_INSECURE`, used to define if cs3 resource access ignore untrusted SSL certificates
* `SEARCH_EXTRACTOR_TIKA_TIKA_URL`, defines the url to the tika installation 

## How Has This Been Tested?
acceptance tests, unit tests, manual testing

## Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [X] New feature (non-breaking change which adds functionality)
- [X] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Technical debt
- [ ] Tests only (no source changes)

## Checklist:
- [X] Code changes
- [X] Unit tests added
- [ ] Acceptance tests added
- [ ] Documentation ticket raised: <link> 


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4305)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [20 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL)

[![52.5%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/50-16px.png '52.5%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list) [52.5% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4305)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [20 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL)

[![54.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/50-16px.png '54.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list) [54.0% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4305)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [20 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL)

[![54.4%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/50-16px.png '54.4%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list) [54.4% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4305)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [13 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL)

[![53.4%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/50-16px.png '53.4%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list) [53.4% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4305)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [13 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL)

[![53.4%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/50-16px.png '53.4%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list) [53.4% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4305)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [13 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL)

[![53.4%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/50-16px.png '53.4%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list) [53.4% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4305)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [13 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL)

[![53.4%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/50-16px.png '53.4%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list) [53.4% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4305)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [13 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL)

[![53.6%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/50-16px.png '53.6%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list) [53.6% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=4305)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=4305&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL) [13 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=4305&resolved=false&types=CODE_SMELL)

[![53.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/50-16px.png '53.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list) [53.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=4305&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/changelog/unreleased/enhancement-search-content-extraction.md b/changelog/unreleased/enhancement-search-content-extraction.md
new file mode 100644
index 00000000000..e7d09dc1f38
--- /dev/null
+++ b/changelog/unreleased/enhancement-search-content-extraction.md
@@ -0,0 +1,8 @@
+Enhancement: Search content extraction
+
+We've added the option to extract content while indexing resources.
+To do so, the configuration flag `SEARCH_EXTRACTOR_TYPE` needs to be set to `tika` and `SEARCH_EXTRACTOR_TIKA_TIKA_URL` must point to a valid tika service url.
+
+It's now also possible to provide different search engines, for the moment, bleve is the only supported engine.
+
+https://github.com/owncloud/ocis/pull/4305
diff --git a/go.mod b/go.mod
index cf221bd10c0..9f9275b29c4 100644
--- a/go.mod
+++ b/go.mod
@@ -6,11 +6,11 @@ require (
 	github.com/CiscoM31/godata v1.0.5
 	github.com/Masterminds/semver v1.5.0
 	github.com/ReneKroon/ttlcache/v2 v2.11.0
+	github.com/bbalet/stopwords v1.0.0
 	github.com/blevesearch/bleve/v2 v2.3.3
-	github.com/blevesearch/bleve_index_api v1.0.2
 	github.com/coreos/go-oidc/v3 v3.2.0
 	github.com/cs3org/go-cs3apis v0.0.0-20220711084433-8f71d4e812a3
-	github.com/cs3org/reva/v2 v2.7.3-0.20220802090121-ae119033c2cc
+	github.com/cs3org/reva/v2 v2.7.3-0.20220802154801-7dd63f110056
 	github.com/disintegration/imaging v1.6.2
 	github.com/ggwhite/go-masker v1.0.9
 	github.com/go-chi/chi/v5 v5.0.7
@@ -35,6 +35,7 @@ require (
 	github.com/gofrs/uuid v4.2.0+incompatible
 	github.com/golang-jwt/jwt/v4 v4.4.2
 	github.com/golang/protobuf v1.5.2
+	github.com/google/go-tika v0.2.0
 	github.com/gookit/config/v2 v2.1.2
 	github.com/gorilla/mux v1.8.0
 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.0
@@ -102,6 +103,7 @@ require (
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bitly/go-simplejson v0.5.0 // indirect
 	github.com/bits-and-blooms/bitset v1.2.1 // indirect
+	github.com/blevesearch/bleve_index_api v1.0.2 // indirect
 	github.com/blevesearch/geo v0.1.12-0.20220606102651-aab42add3121 // indirect
 	github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
 	github.com/blevesearch/gtreap v0.1.1 // indirect
diff --git a/go.sum b/go.sum
index 2642b6e471d..132be1f766f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,10 +1,7 @@
-bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
 bou.ke/monkey v1.0.2 h1:kWcnsrCNUatbxncxR/ThdYqbytgOIArtYWqcQLQzKLI=
-bou.ke/monkey v1.0.2/go.mod h1:OqickVX3tNx6t33n1xvtTtu85YN5s6cKwVug+oHMaIA=
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
 cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
 cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
 cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
@@ -57,7 +54,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-cloud.google.com/go/storage v1.18.2/go.mod h1:AiIj7BWXyhO5gGVmYJ+S8tbkCx3yb0IMjua8Aw4naVM=
 cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA=
 contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
 contrib.go.opencensus.io/exporter/prometheus v0.4.1 h1:oObVeKo2NxpdF/fIfrPsNj6K0Prg0R0mHM+uANlYMiM=
@@ -66,7 +62,6 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
 github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
 github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=
-github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
 github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
@@ -98,7 +93,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
 github.com/CiscoM31/godata v1.0.5 h1:AITXpa/5ybXEq59A0nqUGiS7ZXVJnQtFw5o09tyN/UA=
 github.com/CiscoM31/godata v1.0.5/go.mod h1:wcmFm66GMdOE316TgwFO1wo5ainCvTK26omd93oZf2M=
 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
-github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
 github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
 github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
 github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
@@ -106,12 +100,10 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0
 github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
 github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
 github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
-github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
 github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
 github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
 github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
 github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
-github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
 github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
@@ -129,7 +121,6 @@ github.com/Songmu/prompter v0.5.1/go.mod h1:CS3jEPD6h9IaLaG6afrl1orTgII9+uDWuw95
 github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
 github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
 github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
-github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
 github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -163,16 +154,15 @@ github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:o
 github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
 github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
-github.com/aws/aws-sdk-go v1.20.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
 github.com/aws/aws-sdk-go v1.37.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
 github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
-github.com/aws/aws-sdk-go v1.41.13/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
 github.com/aws/aws-sdk-go v1.43.11/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
-github.com/aws/aws-sdk-go v1.43.28/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 github.com/aws/aws-sdk-go v1.44.61 h1:NcpLSS3Z0MiVQIYugx4I40vSIEEAXT0baO684ExNRco=
 github.com/aws/aws-sdk-go v1.44.61/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
+github.com/bbalet/stopwords v1.0.0 h1:0TnGycCtY0zZi4ltKoOGRFIlZHv0WqpoIGUsObjztfo=
+github.com/bbalet/stopwords v1.0.0/go.mod h1:sAWrQoDMfqARGIn4s6dp7OW7ISrshUD8IP2q3KoqPjc=
 github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
 github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
 github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
@@ -249,11 +239,9 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
 github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 h1:q1g9lSyo/nOIC3W5E3FK3Unrz8b9LdLXCyuC+ZcpPC0=
 github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73/go.mod h1:507vXsotcZop7NZfBWdhPmVeOse4ko2R7AagJYrpoEg=
-github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
 github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
 github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -267,16 +255,6 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
 github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
-github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
-github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
-github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
-github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
-github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
-github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
-github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -289,7 +267,6 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
 github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U=
 github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@@ -304,16 +281,14 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
 github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
 github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo=
 github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
 github.com/crewjam/saml v0.4.6 h1:XCUFPkQSJLvzyl4cW9OvpWUbRf0gE7VUpU8ZnilbeM4=
 github.com/crewjam/saml v0.4.6/go.mod h1:ZBOXnNPFzB3CgOkRm7Nd6IVdkG+l/wF+0ZXLqD96t1A=
-github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
 github.com/cs3org/go-cs3apis v0.0.0-20220711084433-8f71d4e812a3 h1:QSQ2DGKPMChB4vHSs1Os9TnOJl21BrzKX9D5EtQfDog=
 github.com/cs3org/go-cs3apis v0.0.0-20220711084433-8f71d4e812a3/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
-github.com/cs3org/reva/v2 v2.7.3-0.20220802090121-ae119033c2cc h1:rSYb6/4Sh2FOHRsAFNykOSx17f01vk9CTxS5SqupoWs=
-github.com/cs3org/reva/v2 v2.7.3-0.20220802090121-ae119033c2cc/go.mod h1:9FpnWdVFw7ld3iOiuzHHQeCyVH/Ct9h03PZV9ZRplPM=
+github.com/cs3org/reva/v2 v2.7.3-0.20220802154801-7dd63f110056 h1:0wHOfrQfgCCYvGqAxl65BnPI8JfR0CXRpbq/xJxM+Qs=
+github.com/cs3org/reva/v2 v2.7.3-0.20220802154801-7dd63f110056/go.mod h1:9FpnWdVFw7ld3iOiuzHHQeCyVH/Ct9h03PZV9ZRplPM=
 github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI=
 github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY=
 github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
@@ -339,15 +314,12 @@ github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E
 github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
 github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
 github.com/dnsimple/dnsimple-go v0.63.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg=
-github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
-github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg=
 github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -364,7 +336,6 @@ github.com/eternnoir/gncp v0.0.0-20170707042257-c70df2d0cd68 h1:DHBMBKJK69xBWnD/
 github.com/eternnoir/gncp v0.0.0-20170707042257-c70df2d0cd68/go.mod h1:8FuQ7lU9ZvIJGvc04F/qblkjqIfBahAoEFV+XPxByGw=
 github.com/evanphx/json-patch/v5 v5.5.0 h1:bAmFiUJ+o0o2B4OiTFeE3MqCOtyo+jjPP9iZ0VRxYUc=
 github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
-github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59/go.mod h1:XYuK1S5+kS6FGhlIUFuZFPvWiSrOIoLk6+ro33Xce3Y=
 github.com/exoscale/egoscale v0.46.0/go.mod h1:mpEXBpROAa/2i5GC0r33rfxG+TxSEka11g1PIXt9+zc=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
@@ -375,13 +346,11 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga
 github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
-github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
 github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
 github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
-github.com/fsouza/go-dockerclient v1.7.3/go.mod h1:8xfZB8o9SptLNJ13VoV5pMiRbZGWkU/Omu5VOu/KC9Y=
 github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro=
 github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
 github.com/gdexlab/go-render v1.0.1 h1:rxqB3vo5s4n1kF0ySmoNeSPRYkEsyHgln4jFIQY7v0U=
@@ -446,7 +415,6 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-micro/plugins/v4/client/grpc v1.1.0 h1:1MlhnyLxlZpzYY7gepj5zGJNn06yL+3OBbDfh0Z6jH0=
 github.com/go-micro/plugins/v4/client/grpc v1.1.0/go.mod h1:7mskyLlKoKT2V5mC4mdBcrpDx+CiLZxXtTMD+jpFuRY=
-github.com/go-micro/plugins/v4/events/natsjs v1.0.0/go.mod h1:pb7sf8Ych7t7eCuw7jj6ac6rhUrga8SEA55YEFiwmrY=
 github.com/go-micro/plugins/v4/events/natsjs v1.1.0 h1:eQ3JbN0mnLuiExBM29pRi/EhlefWv3DTtdMk3BljAi4=
 github.com/go-micro/plugins/v4/events/natsjs v1.1.0/go.mod h1:ibNwJyP1Z5y8unW4sryIn8EkdwGkrcaURGwm0DVeMcY=
 github.com/go-micro/plugins/v4/logger/zerolog v1.1.0 h1:sG20ktK9YN/h0TlmQXTuL4/yavxBy5QbzifcVARN5ww=
@@ -463,7 +431,6 @@ github.com/go-micro/plugins/v4/registry/nats v1.1.0 h1:oqQzP5P2FkfoYZiBRuCWsKqh4
 github.com/go-micro/plugins/v4/registry/nats v1.1.0/go.mod h1:4tTfa958PiYUOZNBBNoY1awmgfxFcqQOmix8cR3UM+E=
 github.com/go-micro/plugins/v4/server/grpc v1.1.1 h1:7V5K8RTQhzzFsJCPkKXTJr4YrWyIw5xebUTtDY27l3k=
 github.com/go-micro/plugins/v4/server/grpc v1.1.1/go.mod h1:GntDOa4zIHmyBLAvSXP05gK6im+Fw+Dh8fxUk/yiwaI=
-github.com/go-micro/plugins/v4/server/http v1.0.0/go.mod h1:E8eoUONK91jcMpvkcFUsqbgDyKyri3x7ty2GuM5SsI0=
 github.com/go-micro/plugins/v4/server/http v1.1.0 h1:3zigMm74qsuGJDMHCuYFy+2PEvnKND3S5AcabuWWyr8=
 github.com/go-micro/plugins/v4/server/http v1.1.0/go.mod h1:ZjVZi1l16RjzyT3IISirh9BEZdqzaLLwiE/1fvdSbnI=
 github.com/go-micro/plugins/v4/transport/grpc v1.1.0 h1:mXfDYfFQLnVDzjGY3o84oe4prfux9h8txsnA19dKsj8=
@@ -537,7 +504,6 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
 github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs=
 github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
 github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
-github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
 github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
@@ -547,7 +513,6 @@ github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
@@ -557,7 +522,6 @@ github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
 github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
 github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
-github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
 github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -605,7 +569,6 @@ github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
 github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
-github.com/gomodule/redigo v1.8.8/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
 github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
 github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -626,11 +589,12 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
 github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
 github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
+github.com/google/go-tika v0.2.0 h1:+1dnOoJ/pJrko2XH/3Rm5ssG9+ixOgjmPEz94ikUsxI=
+github.com/google/go-tika v0.2.0/go.mod h1:vnMADwNG1A2AJx+ycQgTNMGe3ZG4CZUowEhK2FykumQ=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -648,7 +612,6 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
 github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
@@ -687,7 +650,6 @@ github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
 github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
@@ -711,8 +673,6 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n
 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
 github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
 github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
-github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
-github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw=
 github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@@ -724,7 +684,6 @@ github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YN
 github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
 github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
 github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
-github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
 github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ=
 github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
 github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
@@ -760,7 +719,6 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
 github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
 github.com/hashicorp/serf v0.9.6 h1:uuEX1kLR6aoda1TBttmJQKDLZE1Ob7KN0NPdE7EtCDc=
 github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
-github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
 github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -780,11 +738,9 @@ github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vf
 github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
-github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
 github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
-github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -812,7 +768,6 @@ github.com/juliangruber/go-intersect v1.1.0 h1:sc+y5dCjMMx0pAdYk/N6KBm00tD/f3tq+
 github.com/juliangruber/go-intersect v1.1.0/go.mod h1:WMau+1kAmnlQnKiikekNJbtGtfmILU/mMU6H7AgKbWQ=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
-github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
 github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
 github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
 github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
@@ -822,21 +777,15 @@ github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT
 github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o=
 github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4=
 github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
 github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/kobergj/reva/v2 v2.0.0-20220725134626-f103bcbbd77f h1:1m6dSH3dSY9Fc84bvGFdLQvdAzTfhyl7gEKdev507WM=
-github.com/kobergj/reva/v2 v2.0.0-20220725134626-f103bcbbd77f/go.mod h1:lJIazzcyMxmGgNLeulrBb7pAGScDRKVJbMD+A7bFQ6k=
 github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -904,12 +853,10 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
 github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
@@ -924,21 +871,16 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N
 github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
 github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
 github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
-github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
 github.com/miekg/dns v1.1.46 h1:uzwpxRtSVxtcIZmz/4Uz6/Rn7G11DvsaslXoy5LxQio=
 github.com/miekg/dns v1.1.46/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
-github.com/mileusna/useragent v1.0.2/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc=
 github.com/mileusna/useragent v1.1.0 h1:OWu/SiD0PQKLK//ifevEcK6srDWijAGKNufxEc8ITCU=
 github.com/mileusna/useragent v1.1.0/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc=
 github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
 github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
-github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
 github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
-github.com/minio/minio-go/v7 v7.0.24/go.mod h1:x81+AX5gHSfCSqw7jxRKHvxUXMlE5uKX0Vb75Xk5yYg=
 github.com/minio/minio-go/v7 v7.0.32 h1:p9zXF2g+C1rm9ZMZXVLp4sv3WzON+NSb0IF6WdIWV0g=
 github.com/minio/minio-go/v7 v7.0.32/go.mod h1:/sjRKkKIA75CKh1iu8E3qBy7ktBmCCDGII0zbXGwbUk=
-github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
 github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
 github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@@ -948,7 +890,6 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK
 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
 github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
 github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
 github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
@@ -961,7 +902,6 @@ github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/
 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@@ -969,9 +909,6 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
-github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM=
-github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
-github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -982,21 +919,15 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
-github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
 github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
-github.com/nats-io/jwt/v2 v2.2.1-0.20220113022732-58e87895b296/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
 github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a h1:lem6QCvxR0Y28gth9P+wV2K/zYUUAkJ+55U8cpS0p5I=
 github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
-github.com/nats-io/nats-server/v2 v2.7.4/go.mod h1:1vZ2Nijh8tcyNe8BDVyTviCd9NYzRbubQYiEHsvOQWc=
-github.com/nats-io/nats-server/v2 v2.8.0/go.mod h1:5vic7C58BFEVltiZhs7Kq81q2WcEPhJPsmNv1FOrdv0=
 github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4=
 github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4=
-github.com/nats-io/nats.go v1.13.1-0.20220308171302-2f2f6968e98d/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
-github.com/nats-io/nats.go v1.14.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
 github.com/nats-io/nats.go v1.15.0 h1:3IXNBolWrwIUf2soxh6Rla8gPzYWEZQBUBK6RV21s+o=
 github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
 github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
@@ -1014,7 +945,6 @@ github.com/nrdcg/porkbun v0.1.1/go.mod h1:JWl/WKnguWos4mjfp4YizvvToigk9qpQwrodOk
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
 github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
 github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
@@ -1024,25 +954,15 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
 github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
 github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
-github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
 github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q=
 github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
-github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
-github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
@@ -1070,7 +990,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
 github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
-github.com/pkg/xattr v0.4.5/go.mod h1:sBD3RAqlr8Q+RC3FutZcikpT8nyDrIEEBw2J744gVWs=
 github.com/pkg/xattr v0.4.7 h1:XoA3KzmFvyPlH4RwX5eMcgtzcaGBaSvgt3IoFQfbrmQ=
 github.com/pkg/xattr v0.4.7/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -1120,7 +1039,6 @@ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+
 github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI=
 github.com/prometheus/exporter-toolkit v0.7.1/go.mod h1:ZUBIj498ePooX9t/2xtDjeQYwvRpiPP2lh5u4iblj2g=
 github.com/prometheus/procfs v0.0.0-20170703101242-e645f4e5aaa8/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -1149,10 +1067,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
 github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
-github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
 github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
 github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs=
 github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U=
 github.com/russellhaering/goxmldsig v1.1.1 h1:vI0r2osGF1A9PLvsGdPUAGwEIrKa4Pj5sesSBsebIxM=
@@ -1209,7 +1125,6 @@ github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
@@ -1219,7 +1134,6 @@ github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJ
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -1242,33 +1156,24 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
 github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/studio-b12/gowebdav v0.0.0-20211109083228-3f8721cd4b6f/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
 github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
 github.com/thanhpk/randstr v1.0.4 h1:IN78qu/bR+My+gHCvMEXhR/i5oriVHcTB/BJJIRTsNo=
-github.com/thanhpk/randstr v1.0.4/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U=
 github.com/thejerf/suture/v4 v4.0.2 h1:VxIH/J8uYvqJY1+9fxi5GBfGRkRZ/jlSOP6x9HijFQc=
 github.com/thejerf/suture/v4 v4.0.2/go.mod h1:g0e8vwskm9tI0jRjxrnA6lSr0q6OfPdWJVX7G5bVWRs=
 github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 h1:PM5hJF7HVfNWmCjMdEfbuOBNXSVF2cMFGgQTPdKCbwM=
 github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns=
 github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
 github.com/trustelem/zxcvbn v1.0.1 h1:mp4JFtzdDYGj9WYSD3KQSkwwUumWNFzXaAjckaTYpsc=
 github.com/trustelem/zxcvbn v1.0.1/go.mod h1:zonUyKeh7sw6psPf/e3DtRqkRyZvAbOfjNz/aO7YQ5s=
-github.com/tus/tusd v1.1.0/go.mod h1:3DWPOdeCnjBwKtv98y5dSws3itPqfce5TVa0s59LRiA=
-github.com/tus/tusd v1.8.0/go.mod h1:stZzKpol4qz7lX2HXy/1H526dn5mRnkIICTW2lrh9NM=
 github.com/tus/tusd v1.9.0 h1:wEngl8P/gh9gOfdeyQNsFf6zbAwYYVOnjakVGbYCuvM=
 github.com/tus/tusd v1.9.0/go.mod h1:Bfji+3c6/7FVD7/nK/W9fM7h83d3ILTNWOc6aClR8lo=
 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
 github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
-github.com/uber/jaeger-client-go v2.29.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
-github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
-github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
 github.com/urfave/cli/v2 v2.11.1 h1:UKK6SP7fV3eKOefbS87iT9YHefv7iB/53ih6e+GNAsE=
@@ -1276,14 +1181,12 @@ github.com/urfave/cli/v2 v2.11.1/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhA
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
 github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
-github.com/vimeo/go-util v1.2.0/go.mod h1:s13SMDTSO7AjH1nbgp707mfN5JFIWUFDU5MDDuRRtKs=
 github.com/vimeo/go-util v1.4.1/go.mod h1:r+yspV//C48HeMXV8nEvtUeNiIiGfVv3bbEHzOgudwE=
 github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg=
 github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
 github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
 github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
 github.com/vultr/govultr/v2 v2.0.0/go.mod h1:2PsEeg+gs3p/Fo5Pw8F9mv+DUBEOlrNZ8GmCTGmhOhs=
-github.com/wk8/go-ordered-map v0.2.0/go.mod h1:9ZIbRunKbuvfPKyBP1SIKLcXNlv74YCOZ3t3VTS6gRk=
 github.com/wk8/go-ordered-map v1.0.0 h1:BV7z+2PaK8LTSd/mWgY12HyMAo5CEgkHqbkVq2thqr8=
 github.com/wk8/go-ordered-map v1.0.0/go.mod h1:9ZIbRunKbuvfPKyBP1SIKLcXNlv74YCOZ3t3VTS6gRk=
 github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
@@ -1311,13 +1214,11 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
 github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
 github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
 github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
-go-micro.dev/v4 v4.6.0/go.mod h1:7UY87mLE6T4zHKsNS5D+VWZcXGTEvU1rbA90PezzlWM=
 go-micro.dev/v4 v4.8.0 h1:4rrKpGHUTnrD2k2eVUVayJgWDoFlDcB/Ijkga8dZMNc=
 go-micro.dev/v4 v4.8.0/go.mod h1:Ju8HrZ5hQSF+QguZ2QUs9Kbe42MHP1tJa/fpP5g07Cs=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -1343,23 +1244,14 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.31.0/go.mod h1:SY9qHHUES6W3oZnO1H2W8NvsSovIoXRg/A1AH9px8+I=
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.33.0 h1:z6rnla1Asjzn0FrhohzIbDi4bxbtc6EMmQ7f5ZPn+pA=
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.33.0/go.mod h1:y/SlJpJQPd2UzfBCj0E9Flk9FDCtTyqUmaCB41qFrWI=
-go.opentelemetry.io/otel v1.6.1/go.mod h1:blzUabWHkX6LJewxvadmzafgh/wnvBSDBdOuwkAtrWQ=
-go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI=
-go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
 go.opentelemetry.io/otel v1.8.0 h1:zcvBFizPbpa1q7FehvFiHbQwGzmPILebO0tyqIR5Djg=
 go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM=
-go.opentelemetry.io/otel/exporters/jaeger v1.6.3/go.mod h1:YgX3eZWbJzgrNyNHCK0otGreAMBTIAcObtZS2VRi6sU=
 go.opentelemetry.io/otel/exporters/jaeger v1.8.0 h1:TLLqD6kDhLPziEC7pgPrMvP9lAqdk3n1gf8DiFSnfW8=
 go.opentelemetry.io/otel/exporters/jaeger v1.8.0/go.mod h1:GbWg+ng88rDtx+id26C34QLqw2erqJeAjsCx9AFeHfE=
-go.opentelemetry.io/otel/sdk v1.6.3/go.mod h1:A4iWF7HTXa+GWL/AaqESz28VuSBIcZ+0CV+IzJ5NMiQ=
 go.opentelemetry.io/otel/sdk v1.8.0 h1:xwu69/fNuwbSHWe/0PGS888RmjWY181OmcXDQKu7ZQk=
 go.opentelemetry.io/otel/sdk v1.8.0/go.mod h1:uPSfc+yfDH2StDM/Rm35WE8gXSNdvCg023J6HeGNO0c=
-go.opentelemetry.io/otel/trace v1.6.1/go.mod h1:RkFRM1m0puWIq10oxImnGEduNBzxiN7TXluRBtE+5j0=
-go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs=
-go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
 go.opentelemetry.io/otel/trace v1.8.0 h1:cSy0DF9eGI5WIfNwZ1q2iUyGj00tGzP24dE1lOlHrfY=
 go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
@@ -1408,21 +1300,14 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
 golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
-golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
@@ -1434,7 +1319,6 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf h1:oXVg4h2qJDd9htKxb5SCpFBHLipW6hXmL3qpUixS2jw=
 golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys=
-golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -1466,7 +1350,6 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1482,13 +1365,13 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1518,14 +1401,10 @@ golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5o
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
 golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
-golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@@ -1549,7 +1428,6 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ
 golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
@@ -1605,7 +1483,6 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1615,7 +1492,6 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1633,14 +1509,11 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1648,7 +1521,6 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210216224549-f992740a1bac/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1659,7 +1531,6 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1669,22 +1540,17 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1693,12 +1559,11 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM=
 golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1715,17 +1580,12 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
 golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs=
 golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -1741,7 +1601,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
 golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1791,7 +1650,6 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
 golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
 golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
 golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
@@ -1800,13 +1658,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
-gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
-gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
-gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
 google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -1835,8 +1688,6 @@ google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6
 google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
 google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
 google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
-google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E=
-google.golang.org/api v0.60.0/go.mod h1:d7rl65NZAkEQ90JFzqBjcRq1TVeG5ZoGV3sSpEnnVb4=
 google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
 google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
 google.golang.org/api v0.64.0/go.mod h1:931CdxA8Rm4t6zqTFGSsgwbAEZ2+GMYurbndwSimebM=
@@ -1854,14 +1705,12 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID
 google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
 google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@@ -1913,10 +1762,7 @@ google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEc
 google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211016002631-37fc39342514/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
@@ -1936,14 +1782,12 @@ google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2
 google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
 google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252 h1:G5AjFxR+ibe9Taamo0TdW+iylfBYK10DSkHYdx7PZ9w=
 google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
-google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -1966,7 +1810,6 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
 google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
 google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
 google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
@@ -2000,7 +1843,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
-gopkg.in/h2non/gock.v1 v1.0.14/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
 gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
 gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
 gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -2040,8 +1882,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
-gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
 gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
 gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -2053,7 +1893,6 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/services/search/pkg/config/config.go b/services/search/pkg/config/config.go
index c2394587fc4..31a36c69701 100644
--- a/services/search/pkg/config/config.go
+++ b/services/search/pkg/config/config.go
@@ -18,18 +18,12 @@ type Config struct {
 
 	GRPC GRPC `yaml:"grpc"`
 
-	Datapath string `yaml:"data_path" env:"SEARCH_DATA_PATH" desc:"Path for the search persistence directory."`
-	Reva     *Reva  `yaml:"reva"`
-	Events   Events `yaml:"events"`
+	Reva      *Reva     `yaml:"reva"`
+	Events    Events    `yaml:"events"`
+	Engine    Engine    `yaml:"engine"`
+	Extractor Extractor `yaml:"extractor"`
 
 	MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;SEARCH_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary for the access to resources from other services."`
 
 	Context context.Context `yaml:"-"`
 }
-
-// Events combines the configuration options for the event bus.
-type Events struct {
-	Endpoint      string `yaml:"endpoint" env:"SEARCH_EVENTS_ENDPOINT" desc:"The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture.`
-	Cluster       string `yaml:"cluster" env:"SEARCH_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."`
-	ConsumerGroup string `yaml:"group" env:"SEARCH_EVENTS_GROUP" desc:"The customer group of the service. One group will only get one copy of an event"`
-}
diff --git a/services/search/pkg/config/content.go b/services/search/pkg/config/content.go
new file mode 100644
index 00000000000..8464391744b
--- /dev/null
+++ b/services/search/pkg/config/content.go
@@ -0,0 +1,11 @@
+package config
+
+type Extractor struct {
+	Type             string        `yaml:"type" env:"SEARCH_EXTRACTOR_TYPE" desc:"Defines the content extraction engine."`
+	CS3AllowInsecure bool          `yaml:"cs3_allow_insecure" env:"OCIS_INSECURE;SEARCH_EXTRACTOR_CS3SOURCE_INSECURE" desc:"Ignore untrusted SSL certificates when connecting to the CS3 source."`
+	Tika             ExtractorTika `yaml:"tika"`
+}
+
+type ExtractorTika struct {
+	TikaURL string `yaml:"tika_url" env:"SEARCH_EXTRACTOR_TIKA_TIKA_URL" desc:"URL of the tika server."`
+}
diff --git a/services/search/pkg/config/defaults/defaultconfig.go b/services/search/pkg/config/defaults/defaultconfig.go
index 78690be1861..7bf814699aa 100644
--- a/services/search/pkg/config/defaults/defaultconfig.go
+++ b/services/search/pkg/config/defaults/defaultconfig.go
@@ -28,14 +28,26 @@ func DefaultConfig() *config.Config {
 		Service: config.Service{
 			Name: "search",
 		},
-		Datapath: path.Join(defaults.BaseDataPath(), "search"),
 		Reva: &config.Reva{
 			Address: "127.0.0.1:9142",
 		},
+		Engine: config.Engine{
+			Type: "bleve",
+			Bleve: config.EngineBleve{
+				Datapath: path.Join(defaults.BaseDataPath(), "search"),
+			},
+		},
+		Extractor: config.Extractor{
+			Type:             "basic",
+			CS3AllowInsecure: false,
+			Tika: config.ExtractorTika{
+				TikaURL: "http://127.0.0.1:9998",
+			},
+		},
 		Events: config.Events{
-			Endpoint:      "127.0.0.1:9233",
-			Cluster:       "ocis-cluster",
-			ConsumerGroup: "search",
+			Endpoint:     "127.0.0.1:9233",
+			Cluster:      "ocis-cluster",
+			AsyncUploads: false,
 		},
 		MachineAuthAPIKey: "",
 	}
diff --git a/services/search/pkg/config/engine.go b/services/search/pkg/config/engine.go
new file mode 100644
index 00000000000..96d776ee9b9
--- /dev/null
+++ b/services/search/pkg/config/engine.go
@@ -0,0 +1,10 @@
+package config
+
+type Engine struct {
+	Type  string      `yaml:"type" env:"SEARCH_ENGINE_TYPE" desc:"Defines which search engine to use."`
+	Bleve EngineBleve `yaml:"bleve"`
+}
+
+type EngineBleve struct {
+	Datapath string `yaml:"data_path" env:"SEARCH_ENGINE_BLEVE_DATA_PATH" desc:"Path for the search persistence directory."`
+}
diff --git a/services/search/pkg/config/search.go b/services/search/pkg/config/search.go
new file mode 100644
index 00000000000..b1cb5cee9b4
--- /dev/null
+++ b/services/search/pkg/config/search.go
@@ -0,0 +1,8 @@
+package config
+
+// Events combines the configuration options for the event bus.
+type Events struct {
+	Endpoint     string `yaml:"endpoint" env:"SEARCH_EVENTS_ENDPOINT" desc:"The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture."`
+	Cluster      string `yaml:"cluster" env:"SEARCH_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."`
+	AsyncUploads bool   `yaml:"async_uploads" env:"STORAGE_USERS_OCIS_ASYNC_UPLOADS;SEARCH_EVENTS_ASYNC_UPLOADS" desc:"Enable asynchronous file uploads."`
+}
diff --git a/services/search/pkg/content/basic.go b/services/search/pkg/content/basic.go
new file mode 100644
index 00000000000..db9a652fe28
--- /dev/null
+++ b/services/search/pkg/content/basic.go
@@ -0,0 +1,33 @@
+package content
+
+import (
+	"context"
+	storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"time"
+)
+
+// Basic is the simplest Extractor implementation.
+type Basic struct {
+	logger log.Logger
+}
+
+// NewBasicExtractor creates a new Basic instance.
+func NewBasicExtractor(logger log.Logger) (*Basic, error) {
+	return &Basic{logger: logger}, nil
+}
+
+// Extract literally just rearranges the inputs and processes them into a Document.
+func (b Basic) Extract(ctx context.Context, ref *storageProvider.Reference, ri *storageProvider.ResourceInfo) (Document, error) {
+	doc := Document{
+		Name:     ri.Path,
+		Size:     ri.Size,
+		MimeType: ri.MimeType,
+	}
+
+	if ri.Mtime != nil {
+		doc.Mtime = time.Unix(int64(ri.Mtime.Seconds), int64(ri.Mtime.Nanos)).UTC().Format(time.RFC3339)
+	}
+
+	return doc, nil
+}
diff --git a/services/search/pkg/content/content.go b/services/search/pkg/content/content.go
new file mode 100644
index 00000000000..9ed2aca6ca4
--- /dev/null
+++ b/services/search/pkg/content/content.go
@@ -0,0 +1,12 @@
+package content
+
+// Document wraps all resource meta fields,
+// it is used as a content extraction result.
+type Document struct {
+	Title    string
+	Name     string
+	Content  string
+	Size     uint64
+	Mtime    string
+	MimeType string
+}
diff --git a/services/search/pkg/content/cs3.go b/services/search/pkg/content/cs3.go
new file mode 100644
index 00000000000..ce3fb96f159
--- /dev/null
+++ b/services/search/pkg/content/cs3.go
@@ -0,0 +1,100 @@
+package content
+
+import (
+	"context"
+	"crypto/tls"
+	"fmt"
+	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
+	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
+	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
+	revactx "github.com/cs3org/reva/v2/pkg/ctx"
+	"github.com/cs3org/reva/v2/pkg/errtypes"
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"google.golang.org/grpc/metadata"
+	"io"
+	"net/http"
+)
+
+type cs3 struct {
+	client   gateway.GatewayAPIClient
+	logger   log.Logger
+	insecure bool
+	secret   string
+}
+
+func newCS3Retriever(client gateway.GatewayAPIClient, logger log.Logger, secret string, insecure bool) cs3 {
+	return cs3{
+		client:   client,
+		insecure: insecure,
+		secret:   secret,
+		logger:   logger,
+	}
+}
+
+// Retrieve downloads the file from a cs3 service
+// The caller MUST make sure to close the returned ReadCloser
+func (s cs3) Retrieve(ctx context.Context, ref *provider.Reference, owner *user.User) (io.ReadCloser, error) {
+	authRes, err := s.client.Authenticate(
+		ctxpkg.ContextSetUser(ctx, owner),
+		&gateway.AuthenticateRequest{
+			Type:         "machine",
+			ClientId:     "userid:" + owner.GetId().GetOpaqueId(),
+			ClientSecret: s.secret,
+		},
+	)
+	if err == nil && authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
+		err = errtypes.NewErrtypeFromStatus(authRes.Status)
+	}
+	if err != nil {
+		s.logger.Error().Err(err).Interface("owner", owner).Interface("authRes", authRes).Msg("error using machine auth")
+		return nil, err
+	}
+
+	ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, authRes.Token)
+	res, err := s.client.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{Ref: ref})
+	if err != nil {
+		return nil, err
+	}
+
+	if res.Status.Code != rpc.Code_CODE_OK {
+		return nil, fmt.Errorf("could not load resoure: %s", res.Status.Message)
+	}
+
+	var ep, tk string
+	for _, p := range res.Protocols {
+		if p.Protocol == "spaces" {
+			ep, tk = p.DownloadEndpoint, p.Token
+			break
+		}
+	}
+	if (ep == "" || tk == "") && len(res.Protocols) > 0 {
+		ep, tk = res.Protocols[0].DownloadEndpoint, res.Protocols[0].Token
+	}
+
+	req, err := http.NewRequest("GET", ep, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	req.Header.Set(revactx.TokenHeader, authRes.Token)
+	req.Header.Set("X-Reva-Transfer", tk)
+
+	client := http.Client{
+		Transport: &http.Transport{
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: s.insecure},
+		},
+	}
+
+	cres, err := client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+
+	if cres.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("could not get the resource \"%s\". Request returned with statuscode %d ", ref.Path, cres.StatusCode)
+	}
+
+	return cres.Body, nil
+}
diff --git a/services/search/pkg/content/extractor.go b/services/search/pkg/content/extractor.go
new file mode 100644
index 00000000000..a32ae8a8a13
--- /dev/null
+++ b/services/search/pkg/content/extractor.go
@@ -0,0 +1,31 @@
+package content
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+)
+
+//go:generate mockery --name=Extractor
+// Extractor is responsible to extract content and meta information from documents.
+type Extractor interface {
+	Extract(ctx context.Context, ref *provider.Reference, ri *provider.ResourceInfo) (Document, error)
+}
+
+func getFirstValue(m map[string][]string, key string) (string, error) {
+	if m == nil {
+		return "", errors.New("undefined map")
+	}
+
+	v, ok := m[key]
+	if !ok {
+		return "", fmt.Errorf("unknown key: %v", key)
+	}
+
+	if len(m) == 0 {
+		return "", fmt.Errorf("no values for: %v", key)
+	}
+
+	return v[0], nil
+}
diff --git a/services/search/pkg/content/mocks/Extractor.go b/services/search/pkg/content/mocks/Extractor.go
new file mode 100644
index 00000000000..c1b16c8cfe8
--- /dev/null
+++ b/services/search/pkg/content/mocks/Extractor.go
@@ -0,0 +1,54 @@
+// Code generated by mockery v2.14.0. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	content "github.com/owncloud/ocis/v2/services/search/pkg/content"
+
+	mock "github.com/stretchr/testify/mock"
+
+	providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+)
+
+// Extractor is an autogenerated mock type for the Extractor type
+type Extractor struct {
+	mock.Mock
+}
+
+// Extract provides a mock function with given fields: ctx, ref, ri
+func (_m *Extractor) Extract(ctx context.Context, ref *providerv1beta1.Reference, ri *providerv1beta1.ResourceInfo) (content.Document, error) {
+	ret := _m.Called(ctx, ref, ri)
+
+	var r0 content.Document
+	if rf, ok := ret.Get(0).(func(context.Context, *providerv1beta1.Reference, *providerv1beta1.ResourceInfo) content.Document); ok {
+		r0 = rf(ctx, ref, ri)
+	} else {
+		r0 = ret.Get(0).(content.Document)
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *providerv1beta1.Reference, *providerv1beta1.ResourceInfo) error); ok {
+		r1 = rf(ctx, ref, ri)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+type mockConstructorTestingTNewExtractor interface {
+	mock.TestingT
+	Cleanup(func())
+}
+
+// NewExtractor creates a new instance of Extractor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+func NewExtractor(t mockConstructorTestingTNewExtractor) *Extractor {
+	mock := &Extractor{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/services/search/pkg/content/mocks/Retriever.go b/services/search/pkg/content/mocks/Retriever.go
new file mode 100644
index 00000000000..ce3a4c6abac
--- /dev/null
+++ b/services/search/pkg/content/mocks/Retriever.go
@@ -0,0 +1,57 @@
+// Code generated by mockery v2.14.0. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+	io "io"
+
+	mock "github.com/stretchr/testify/mock"
+
+	providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+
+	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+)
+
+// Retriever is an autogenerated mock type for the Retriever type
+type Retriever struct {
+	mock.Mock
+}
+
+// Retrieve provides a mock function with given fields: ctx, ref, owner
+func (_m *Retriever) Retrieve(ctx context.Context, ref *providerv1beta1.Reference, owner *userv1beta1.User) (io.ReadCloser, error) {
+	ret := _m.Called(ctx, ref, owner)
+
+	var r0 io.ReadCloser
+	if rf, ok := ret.Get(0).(func(context.Context, *providerv1beta1.Reference, *userv1beta1.User) io.ReadCloser); ok {
+		r0 = rf(ctx, ref, owner)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(io.ReadCloser)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *providerv1beta1.Reference, *userv1beta1.User) error); ok {
+		r1 = rf(ctx, ref, owner)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+type mockConstructorTestingTNewRetriever interface {
+	mock.TestingT
+	Cleanup(func())
+}
+
+// NewRetriever creates a new instance of Retriever. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+func NewRetriever(t mockConstructorTestingTNewRetriever) *Retriever {
+	mock := &Retriever{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/services/search/pkg/content/retriever.go b/services/search/pkg/content/retriever.go
new file mode 100644
index 00000000000..1179635bc55
--- /dev/null
+++ b/services/search/pkg/content/retriever.go
@@ -0,0 +1,15 @@
+package content
+
+import (
+	"context"
+	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	"io"
+)
+
+//go:generate mockery --name=Retriever
+// Retriever is the interface that wraps the basic Retrieve method. 🐕
+// It requests and then returns a resource from the underlying storage.
+type Retriever interface {
+	Retrieve(ctx context.Context, ref *provider.Reference, owner *user.User) (io.ReadCloser, error)
+}
diff --git a/services/search/pkg/content/tika.go b/services/search/pkg/content/tika.go
new file mode 100644
index 00000000000..3e4d3b7bd87
--- /dev/null
+++ b/services/search/pkg/content/tika.go
@@ -0,0 +1,90 @@
+package content
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"github.com/bbalet/stopwords"
+	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
+	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	"github.com/google/go-tika/tika"
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"github.com/owncloud/ocis/v2/services/search/pkg/config"
+	"io"
+	"strings"
+)
+
+// Tika is used to extract content from a resource,
+// it uses apache tika to retrieve all the data.
+type Tika struct {
+	*Basic
+	Retriever
+	tika *tika.Client
+}
+
+// NewTikaExtractor creates a new Tika instance.
+func NewTikaExtractor(gw gateway.GatewayAPIClient, logger log.Logger, cfg *config.Config) (*Tika, error) {
+	basic, err := NewBasicExtractor(logger)
+	if err != nil {
+		return nil, err
+	}
+
+	tk := tika.NewClient(nil, cfg.Extractor.Tika.TikaURL)
+	tkv, err := tk.Version(context.Background())
+	if err != nil {
+		return nil, err
+	}
+	logger.Info().Msg(fmt.Sprintf("Tika version: %s", tkv))
+
+	return &Tika{
+		Basic:     basic,
+		Retriever: newCS3Retriever(gw, logger, cfg.MachineAuthAPIKey, cfg.Extractor.CS3AllowInsecure),
+		tika:      tika.NewClient(nil, cfg.Extractor.Tika.TikaURL),
+	}, nil
+}
+
+// Extract loads a resource from its underlying storage, passes it to tika and processes the result into a Document.
+func (t Tika) Extract(ctx context.Context, ref *provider.Reference, ri *provider.ResourceInfo) (Document, error) {
+	doc, err := t.Basic.Extract(ctx, ref, ri)
+	if err != nil {
+		return doc, err
+	}
+
+	data, err := t.Retrieve(ctx, ref, &user.User{Id: ri.Owner})
+	if err != nil {
+		return doc, err
+	}
+	defer data.Close()
+
+	var d1, d2 bytes.Buffer
+	if _, err := io.Copy(io.MultiWriter(&d1, &d2), data); err != nil {
+		return doc, err
+	}
+
+	lang, err := t.tika.Language(ctx, bytes.NewReader(d1.Bytes()))
+	if err != nil {
+		return doc, nil
+	}
+
+	metas, err := t.tika.MetaRecursive(ctx, bytes.NewReader(d2.Bytes()))
+	if err != nil {
+		return doc, err
+	}
+
+	for _, meta := range metas {
+		if title, err := getFirstValue(meta, "title"); err == nil {
+			doc.Title = strings.TrimSpace(fmt.Sprintf("%s %s", doc.Title, title))
+		}
+
+		if content, err := getFirstValue(meta, "X-TIKA:content"); err == nil {
+			if lang != "" {
+				content = stopwords.CleanString(content, lang, true)
+			}
+
+			doc.Content = strings.TrimSpace(fmt.Sprintf("%s %s", doc.Content, content))
+		}
+	}
+
+	return doc, nil
+}
diff --git a/services/search/pkg/engine/bleve.go b/services/search/pkg/engine/bleve.go
new file mode 100644
index 00000000000..b2378d2c49d
--- /dev/null
+++ b/services/search/pkg/engine/bleve.go
@@ -0,0 +1,310 @@
+package engine
+
+import (
+	"context"
+	"errors"
+	"github.com/blevesearch/bleve/v2"
+	"github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
+	"github.com/blevesearch/bleve/v2/analysis/analyzer/keyword"
+	"github.com/blevesearch/bleve/v2/analysis/token/lowercase"
+	"github.com/blevesearch/bleve/v2/analysis/tokenizer/single"
+	"github.com/blevesearch/bleve/v2/mapping"
+	storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	"github.com/cs3org/reva/v2/pkg/storagespace"
+	"github.com/cs3org/reva/v2/pkg/utils"
+	searchMessage "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
+	searchService "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
+	"github.com/owncloud/ocis/v2/services/search/pkg/content"
+	"google.golang.org/protobuf/types/known/timestamppb"
+	"math"
+	"path"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+// Bleve represents a search engine which utilizes bleve to search and store resources.
+type Bleve struct {
+	index bleve.Index
+}
+
+// NewBleveIndex returns a new bleve index
+// given path must exist.
+func NewBleveIndex(root string) (bleve.Index, error) {
+	destination := filepath.Join(root, "index.bleve")
+	index, err := bleve.Open(destination)
+	if err != nil {
+		m, err := BuildBleveMapping()
+		if err != nil {
+			return nil, err
+		}
+		index, err = bleve.New(destination, m)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return index, nil
+}
+
+// NewBleveEngine creates a new Bleve instance
+func NewBleveEngine(index bleve.Index) *Bleve {
+	return &Bleve{
+		index: index,
+	}
+}
+
+// BuildBleveMapping builds a bleve index mapping which can be used for indexing
+func BuildBleveMapping() (mapping.IndexMapping, error) {
+	nameMapping := bleve.NewTextFieldMapping()
+	nameMapping.Analyzer = "lowercaseKeyword"
+
+	docMapping := bleve.NewDocumentMapping()
+	docMapping.AddFieldMappingsAt("Name", nameMapping)
+
+	indexMapping := bleve.NewIndexMapping()
+	indexMapping.DefaultAnalyzer = keyword.Name
+	indexMapping.DefaultMapping = docMapping
+	err := indexMapping.AddCustomAnalyzer("lowercaseKeyword",
+		map[string]interface{}{
+			"type":      custom.Name,
+			"tokenizer": single.Name,
+			"token_filters": []string{
+				lowercase.Name,
+			},
+		})
+	if err != nil {
+		return nil, err
+	}
+
+	return indexMapping, nil
+}
+
+// Search executes a search request operation within the index.
+// Returns a SearchIndexResponse object or an error.
+func (b *Bleve) Search(ctx context.Context, sir *searchService.SearchIndexRequest) (*searchService.SearchIndexResponse, error) {
+	deletedQuery := bleve.NewBoolFieldQuery(false)
+	deletedQuery.SetField("Deleted")
+	query := bleve.NewConjunctionQuery(
+		bleve.NewQueryStringQuery(b.buildQuery(sir.Query)),
+		deletedQuery, // Skip documents that have been marked as deleted
+		bleve.NewQueryStringQuery("RootID:"+storagespace.FormatResourceID(
+			storageProvider.ResourceId{
+				StorageId: sir.Ref.GetResourceId().GetStorageId(),
+				SpaceId:   sir.Ref.GetResourceId().GetSpaceId(),
+				OpaqueId:  sir.Ref.GetResourceId().GetOpaqueId(),
+			})), // Limit search to the space
+		bleve.NewQueryStringQuery("Path:"+escapeQuery(utils.MakeRelativePath(path.Join(sir.Ref.Path, "/"))+"*")), // Limit search to this directory in the space
+	)
+
+	bleveReq := bleve.NewSearchRequest(query)
+	bleveReq.Size = 200
+	if sir.PageSize > 0 {
+		bleveReq.Size = int(sir.PageSize)
+	}
+	bleveReq.Fields = []string{"*"}
+	res, err := b.index.Search(bleveReq)
+	if err != nil {
+		return nil, err
+	}
+
+	matches := []*searchMessage.Match{}
+	for _, hit := range res.Hits {
+		rootID, err := storagespace.ParseID(getValue[string](hit.Fields, "RootID"))
+		if err != nil {
+			return nil, err
+		}
+
+		rID, err := storagespace.ParseID(getValue[string](hit.Fields, "ID"))
+		if err != nil {
+			return nil, err
+		}
+
+		match := &searchMessage.Match{
+			Score: float32(hit.Score),
+			Entity: &searchMessage.Entity{
+				Ref: &searchMessage.Reference{
+					ResourceId: resourceIDtoSearchID(rootID),
+					Path:       getValue[string](hit.Fields, "Path"),
+				},
+				Id:       resourceIDtoSearchID(rID),
+				Name:     getValue[string](hit.Fields, "Name"),
+				Size:     uint64(getValue[float64](hit.Fields, "Size")),
+				Type:     uint64(getValue[float64](hit.Fields, "Type")),
+				MimeType: getValue[string](hit.Fields, "MimeType"),
+				Deleted:  getValue[bool](hit.Fields, "Deleted"),
+			},
+		}
+
+		if mtime, err := time.Parse(time.RFC3339, getValue[string](hit.Fields, "Mtime")); err == nil {
+			match.Entity.LastModifiedTime = &timestamppb.Timestamp{Seconds: mtime.Unix(), Nanos: int32(mtime.Nanosecond())}
+		}
+
+		matches = append(matches, match)
+	}
+
+	return &searchService.SearchIndexResponse{
+		Matches:      matches,
+		TotalMatches: int32(res.Total),
+	}, nil
+}
+
+// Upsert indexes or stores Resource data fields.
+func (b *Bleve) Upsert(id string, r Resource) error {
+	return b.index.Index(id, r)
+}
+
+// Move updates the resource location and all of its necessary fields.
+func (b *Bleve) Move(id string, target string) error {
+	r, err := b.getResource(id)
+	if err != nil {
+		return err
+	}
+	oldName := r.Path
+	newName := utils.MakeRelativePath(target)
+
+	r, err = b.updateEntity(id, func(r *Resource) {
+		r.Path = newName
+		r.Name = path.Base(newName)
+	})
+	if err != nil {
+		return err
+	}
+
+	if r.Type == uint64(storageProvider.ResourceType_RESOURCE_TYPE_CONTAINER) {
+		query := bleve.NewConjunctionQuery(
+			bleve.NewQueryStringQuery("RootID:"+r.RootID),
+			bleve.NewQueryStringQuery("Path:"+escapeQuery(oldName+"/*")),
+		)
+		bleveReq := bleve.NewSearchRequest(query)
+		bleveReq.Size = math.MaxInt
+		bleveReq.Fields = []string{"*"}
+		res, err := b.index.Search(bleveReq)
+		if err != nil {
+			return err
+		}
+
+		for _, h := range res.Hits {
+			_, err := b.updateEntity(h.ID, func(r *Resource) {
+				r.Path = strings.Replace(r.Path, oldName, newName, 1)
+			})
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+// Delete marks the resource as deleted.
+// The resource object will stay in the bleve index,
+// instead of removing the resource it just marks it as deleted!
+// can be undone
+func (b *Bleve) Delete(id string) error {
+	return b.setDeleted(id, true)
+}
+
+// Restore is the counterpart to Delete.
+// It restores the resource which makes it available again.
+func (b *Bleve) Restore(id string) error {
+	return b.setDeleted(id, false)
+}
+
+// Purge removes a resource from the index, irreversible operation.
+func (b *Bleve) Purge(id string) error {
+	return b.index.Delete(id)
+}
+
+// DocCount returns the number of resources in the index.
+func (b *Bleve) DocCount() (uint64, error) {
+	return b.index.DocCount()
+}
+
+func (b *Bleve) getResource(id string) (*Resource, error) {
+	req := bleve.NewSearchRequest(bleve.NewDocIDQuery([]string{id}))
+	req.Fields = []string{"*"}
+	res, err := b.index.Search(req)
+	if err != nil {
+		return nil, err
+	}
+	if res.Hits.Len() == 0 {
+		return nil, errors.New("entity not found")
+	}
+
+	fields := res.Hits[0].Fields
+
+	return &Resource{
+		ID:      getValue[string](fields, "ID"),
+		RootID:  getValue[string](fields, "RootID"),
+		Path:    getValue[string](fields, "Path"),
+		Type:    uint64(getValue[float64](fields, "Type")),
+		Deleted: getValue[bool](fields, "Deleted"),
+		Document: content.Document{
+			Name:     getValue[string](fields, "Name"),
+			Size:     uint64(getValue[float64](fields, "Size")),
+			Mtime:    getValue[string](fields, "Mtime"),
+			MimeType: getValue[string](fields, "MimeType"),
+		},
+	}, nil
+}
+
+func (b *Bleve) updateEntity(id string, mutateFunc func(r *Resource)) (*Resource, error) {
+	it, err := b.getResource(id)
+	if err != nil {
+		return nil, err
+	}
+
+	mutateFunc(it)
+
+	return it, b.index.Index(it.ID, it)
+}
+
+func (b *Bleve) setDeleted(id string, deleted bool) error {
+	it, err := b.updateEntity(id, func(r *Resource) {
+		r.Deleted = deleted
+	})
+	if err != nil {
+		return err
+	}
+
+	if it.Type == uint64(storageProvider.ResourceType_RESOURCE_TYPE_CONTAINER) {
+		query := bleve.NewConjunctionQuery(
+			bleve.NewQueryStringQuery("RootID:"+it.RootID),
+			bleve.NewQueryStringQuery("Path:"+escapeQuery(it.Path+"/*")),
+		)
+		bleveReq := bleve.NewSearchRequest(query)
+		bleveReq.Size = math.MaxInt
+		bleveReq.Fields = []string{"*"}
+		res, err := b.index.Search(bleveReq)
+		if err != nil {
+			return err
+		}
+
+		for _, h := range res.Hits {
+			_, err := b.updateEntity(h.ID, func(r *Resource) {
+				r.Deleted = deleted
+			})
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+func (b Bleve) buildQuery(q string) string {
+	query := q
+	fields := []string{"RootID", "Path", "ID", "Name", "Size", "Mtime", "MimeType", "Type", "Content", "Title"}
+	for _, field := range fields {
+		query = strings.ReplaceAll(query, strings.ToLower(field)+":", field+":")
+	}
+
+	if strings.Contains(query, ":") {
+		return query // Sophisticated field based search
+	}
+
+	// this is a basic filename search
+	return "Name:*" + strings.ReplaceAll(strings.ToLower(query), " ", `\ `) + "*"
+}
diff --git a/services/search/pkg/engine/engine.go b/services/search/pkg/engine/engine.go
new file mode 100644
index 00000000000..97184518dd3
--- /dev/null
+++ b/services/search/pkg/engine/engine.go
@@ -0,0 +1,57 @@
+package engine
+
+import (
+	"context"
+	storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	searchMessage "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
+	searchSearvice "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
+	"github.com/owncloud/ocis/v2/services/search/pkg/content"
+	"regexp"
+)
+
+//go:generate mockery --name=Engine
+
+// Engine is the interface to the search engine
+type Engine interface {
+	Search(ctx context.Context, req *searchSearvice.SearchIndexRequest) (*searchSearvice.SearchIndexResponse, error)
+	Upsert(id string, r Resource) error
+	Move(id string, target string) error
+	Delete(id string) error
+	Restore(id string) error
+	Purge(id string) error
+	DocCount() (uint64, error)
+}
+
+// Resource is the entity that is stored in the index.
+type Resource struct {
+	content.Document
+
+	ID      string
+	RootID  string
+	Path    string
+	Type    uint64
+	Deleted bool
+}
+
+func resourceIDtoSearchID(id storageProvider.ResourceId) *searchMessage.ResourceID {
+	return &searchMessage.ResourceID{
+		StorageId: id.GetStorageId(),
+		SpaceId:   id.GetSpaceId(),
+		OpaqueId:  id.GetOpaqueId()}
+}
+
+func escapeQuery(s string) string {
+	re := regexp.MustCompile(`([` + regexp.QuoteMeta(`+=&|><!(){}[]^\"~*?:\/`) + `\-\s])`)
+	return re.ReplaceAllString(s, "\\$1")
+}
+
+func getValue[T any](m map[string]interface{}, key string) (out T) {
+	val, ok := m[key]
+	if !ok {
+		return
+	}
+
+	out, _ = val.(T)
+
+	return
+}
diff --git a/services/search/pkg/search/mocks/IndexClient.go b/services/search/pkg/engine/mocks/Engine.go
similarity index 51%
rename from services/search/pkg/search/mocks/IndexClient.go
rename to services/search/pkg/engine/mocks/Engine.go
index d2286517a43..274d780b7b0 100644
--- a/services/search/pkg/search/mocks/IndexClient.go
+++ b/services/search/pkg/engine/mocks/Engine.go
@@ -1,41 +1,27 @@
-// Code generated by mockery v2.10.0. DO NOT EDIT.
+// Code generated by mockery v2.14.0. DO NOT EDIT.
 
 package mocks
 
 import (
 	context "context"
 
-	providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	engine "github.com/owncloud/ocis/v2/services/search/pkg/engine"
 	mock "github.com/stretchr/testify/mock"
 
 	v0 "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
 )
 
-// IndexClient is an autogenerated mock type for the IndexClient type
-type IndexClient struct {
+// Engine is an autogenerated mock type for the Engine type
+type Engine struct {
 	mock.Mock
 }
 
-// Add provides a mock function with given fields: ref, ri
-func (_m *IndexClient) Add(ref *providerv1beta1.Reference, ri *providerv1beta1.ResourceInfo) error {
-	ret := _m.Called(ref, ri)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(*providerv1beta1.Reference, *providerv1beta1.ResourceInfo) error); ok {
-		r0 = rf(ref, ri)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
 // Delete provides a mock function with given fields: id
-func (_m *IndexClient) Delete(id *providerv1beta1.ResourceId) error {
+func (_m *Engine) Delete(id string) error {
 	ret := _m.Called(id)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId) error); ok {
+	if rf, ok := ret.Get(0).(func(string) error); ok {
 		r0 = rf(id)
 	} else {
 		r0 = ret.Error(0)
@@ -45,7 +31,7 @@ func (_m *IndexClient) Delete(id *providerv1beta1.ResourceId) error {
 }
 
 // DocCount provides a mock function with given fields:
-func (_m *IndexClient) DocCount() (uint64, error) {
+func (_m *Engine) DocCount() (uint64, error) {
 	ret := _m.Called()
 
 	var r0 uint64
@@ -65,13 +51,13 @@ func (_m *IndexClient) DocCount() (uint64, error) {
 	return r0, r1
 }
 
-// Move provides a mock function with given fields: id, fullPath
-func (_m *IndexClient) Move(id *providerv1beta1.ResourceId, fullPath string) error {
-	ret := _m.Called(id, fullPath)
+// Move provides a mock function with given fields: id, target
+func (_m *Engine) Move(id string, target string) error {
+	ret := _m.Called(id, target)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId, string) error); ok {
-		r0 = rf(id, fullPath)
+	if rf, ok := ret.Get(0).(func(string, string) error); ok {
+		r0 = rf(id, target)
 	} else {
 		r0 = ret.Error(0)
 	}
@@ -80,11 +66,11 @@ func (_m *IndexClient) Move(id *providerv1beta1.ResourceId, fullPath string) err
 }
 
 // Purge provides a mock function with given fields: id
-func (_m *IndexClient) Purge(id *providerv1beta1.ResourceId) error {
+func (_m *Engine) Purge(id string) error {
 	ret := _m.Called(id)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId) error); ok {
+	if rf, ok := ret.Get(0).(func(string) error); ok {
 		r0 = rf(id)
 	} else {
 		r0 = ret.Error(0)
@@ -94,11 +80,11 @@ func (_m *IndexClient) Purge(id *providerv1beta1.ResourceId) error {
 }
 
 // Restore provides a mock function with given fields: id
-func (_m *IndexClient) Restore(id *providerv1beta1.ResourceId) error {
+func (_m *Engine) Restore(id string) error {
 	ret := _m.Called(id)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId) error); ok {
+	if rf, ok := ret.Get(0).(func(string) error); ok {
 		r0 = rf(id)
 	} else {
 		r0 = ret.Error(0)
@@ -108,7 +94,7 @@ func (_m *IndexClient) Restore(id *providerv1beta1.ResourceId) error {
 }
 
 // Search provides a mock function with given fields: ctx, req
-func (_m *IndexClient) Search(ctx context.Context, req *v0.SearchIndexRequest) (*v0.SearchIndexResponse, error) {
+func (_m *Engine) Search(ctx context.Context, req *v0.SearchIndexRequest) (*v0.SearchIndexResponse, error) {
 	ret := _m.Called(ctx, req)
 
 	var r0 *v0.SearchIndexResponse
@@ -129,3 +115,32 @@ func (_m *IndexClient) Search(ctx context.Context, req *v0.SearchIndexRequest) (
 
 	return r0, r1
 }
+
+// Upsert provides a mock function with given fields: id, r
+func (_m *Engine) Upsert(id string, r engine.Resource) error {
+	ret := _m.Called(id, r)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(string, engine.Resource) error); ok {
+		r0 = rf(id, r)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+type mockConstructorTestingTNewEngine interface {
+	mock.TestingT
+	Cleanup(func())
+}
+
+// NewEngine creates a new instance of Engine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+func NewEngine(t mockConstructorTestingTNewEngine) *Engine {
+	mock := &Engine{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/services/search/pkg/logging/logging.go b/services/search/pkg/logging/logging.go
index 28f1aebef13..50cac74c296 100644
--- a/services/search/pkg/logging/logging.go
+++ b/services/search/pkg/logging/logging.go
@@ -5,7 +5,7 @@ import (
 	"github.com/owncloud/ocis/v2/services/search/pkg/config"
 )
 
-// LoggerFromConfig initializes a service-specific logger instance.
+// Configure initializes a service-specific logger instance.
 func Configure(name string, cfg *config.Log) log.Logger {
 	return log.NewLogger(
 		log.Name(name),
diff --git a/services/search/pkg/search/events.go b/services/search/pkg/search/events.go
new file mode 100644
index 00000000000..07542070337
--- /dev/null
+++ b/services/search/pkg/search/events.go
@@ -0,0 +1,182 @@
+package search
+
+import (
+	"context"
+	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
+	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
+	rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
+	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	"github.com/cs3org/reva/v2/pkg/events"
+	"github.com/cs3org/reva/v2/pkg/storagespace"
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"github.com/owncloud/ocis/v2/services/search/pkg/config"
+	"github.com/owncloud/ocis/v2/services/search/pkg/content"
+	"github.com/owncloud/ocis/v2/services/search/pkg/engine"
+)
+
+type eventHandler struct {
+	logger    log.Logger
+	engine    engine.Engine
+	gateway   gateway.GatewayAPIClient
+	extractor content.Extractor
+	secret    string
+}
+
+// HandleEvents listens to the needed events,
+// it handles the whole resource indexing livecycle.
+func HandleEvents(eng engine.Engine, extractor content.Extractor, gw gateway.GatewayAPIClient, bus events.Stream, logger log.Logger, cfg *config.Config) error {
+	ch, err := events.Consume(
+		bus,
+		"search",
+		events.ItemTrashed{},
+		events.ItemRestored{},
+		events.ItemMoved{},
+		events.ContainerCreated{},
+		events.FileTouched{},
+		events.FileVersionRestored{},
+		events.FileUploaded{},
+		events.UploadReady{},
+	)
+	if err != nil {
+		return err
+	}
+
+	go func(eh *eventHandler, ch <-chan interface{}) {
+		for e := range ch {
+
+			eh.logger.Debug().Interface("event", e).Msg("updating index")
+
+			switch ev := e.(type) {
+			case events.ItemTrashed:
+				eh.trashItem(ev.ID)
+			case events.ItemMoved:
+				eh.moveItem(ev.Ref, &user.User{Id: ev.Executant})
+			case events.ItemRestored:
+				eh.restoreItem(ev.Ref, &user.User{Id: ev.Executant})
+			case events.ContainerCreated:
+				eh.upsertItem(ev.Ref, &user.User{Id: ev.Executant})
+			case events.FileTouched:
+				eh.upsertItem(ev.Ref, &user.User{Id: ev.Executant})
+			case events.FileVersionRestored:
+				eh.upsertItem(ev.Ref, &user.User{Id: ev.Executant})
+			case events.FileUploaded:
+				if cfg.Events.AsyncUploads {
+					return
+				}
+
+				eh.upsertItem(ev.Ref, &user.User{Id: ev.Executant})
+			case events.UploadReady:
+				eh.upsertItem(ev.FileRef, ev.ExecutingUser)
+			}
+		}
+	}(
+		&eventHandler{
+			logger:    logger,
+			engine:    eng,
+			secret:    cfg.MachineAuthAPIKey,
+			gateway:   gw,
+			extractor: extractor,
+		},
+		ch,
+	)
+
+	return nil
+}
+
+func (eh *eventHandler) trashItem(rid *provider.ResourceId) {
+	err := eh.engine.Delete(storagespace.FormatResourceID(*rid))
+	if err != nil {
+		eh.logger.Error().Err(err).Interface("Id", rid).Msg("failed to remove item from index")
+	}
+}
+
+func (eh *eventHandler) upsertItem(ref *provider.Reference, owner *user.User) {
+	if ref == nil || owner == nil {
+		return
+	}
+
+	statRes, err := statResource(ref, owner, eh.gateway, eh.secret, eh.logger)
+	if err != nil {
+		eh.logger.Error().Err(err).Msg("failed to stat the changed resource")
+		return
+	}
+	if statRes.Status.Code != rpc.Code_CODE_OK {
+		eh.logger.Error().Interface("statRes", statRes).Msg("failed to stat the changed resource")
+		return
+	}
+
+	doc, err := eh.extractor.Extract(context.TODO(), ref, statRes.Info)
+	if err != nil {
+		eh.logger.Error().Err(err).Msg("failed to extract resource content")
+		return
+	}
+
+	r := engine.Resource{
+		ID:       storagespace.FormatResourceID(*statRes.Info.Id),
+		RootID:   storagespace.FormatResourceID(*ref.ResourceId),
+		Path:     ref.Path,
+		Type:     uint64(statRes.Info.Type),
+		Document: doc,
+	}
+
+	err = eh.engine.Upsert(r.ID, r)
+	if err != nil {
+		eh.logger.Error().Err(err).Msg("error adding updating the resource in the index")
+	} else {
+		logDocCount(eh.engine, eh.logger)
+	}
+}
+
+func (eh *eventHandler) restoreItem(ref *provider.Reference, owner *user.User) {
+	if ref == nil || owner == nil {
+		return
+	}
+
+	statRes, err := statResource(ref, owner, eh.gateway, eh.secret, eh.logger)
+	if err != nil {
+		eh.logger.Error().Err(err).Msg("failed to stat the changed resource")
+		return
+	}
+
+	if statRes.Status.Code != rpc.Code_CODE_OK {
+		eh.logger.Error().Interface("statRes", statRes).Msg("failed to stat the changed resource")
+		return
+	}
+
+	err = eh.engine.Restore(storagespace.FormatResourceID(*statRes.Info.Id))
+	if err != nil {
+		eh.logger.Error().Err(err).Msg("failed to restore the changed resource in the index")
+	}
+}
+
+func (eh *eventHandler) moveItem(ref *provider.Reference, owner *user.User) {
+	if ref == nil || owner == nil {
+		return
+	}
+
+	statRes, err := statResource(ref, owner, eh.gateway, eh.secret, eh.logger)
+	if err != nil {
+		eh.logger.Error().Err(err).Msg("failed to stat the moved resource")
+		return
+	}
+	if statRes.Status.Code != rpc.Code_CODE_OK {
+		eh.logger.Error().Interface("statRes", statRes).Msg("failed to stat the moved resource")
+		return
+	}
+
+	gpRes, err := getPath(statRes.Info.Id, owner, eh.gateway, eh.secret, eh.logger)
+	if err != nil {
+		eh.logger.Error().Err(err).Interface("ref", ref).Msg("failed to get path for moved resource")
+		return
+	}
+	if gpRes.Status.Code != rpcv1beta1.Code_CODE_OK {
+		eh.logger.Error().Interface("status", gpRes.Status).Interface("ref", ref).Msg("failed to get path for moved resource")
+		return
+	}
+
+	err = eh.engine.Move(storagespace.FormatResourceID(*statRes.Info.Id), gpRes.Path)
+	if err != nil {
+		eh.logger.Error().Err(err).Msg("failed to move the changed resource in the index")
+	}
+}
diff --git a/services/search/pkg/search/index/index.go b/services/search/pkg/search/index/index.go
deleted file mode 100644
index 271557e6ff9..00000000000
--- a/services/search/pkg/search/index/index.go
+++ /dev/null
@@ -1,366 +0,0 @@
-// Copyright 2018-2022 CERN
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// In applying this license, CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-package index
-
-import (
-	"context"
-	"errors"
-	"math"
-	"path"
-	"regexp"
-	"strings"
-	"time"
-
-	bleve "github.com/blevesearch/bleve/v2"
-	"github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
-	"github.com/blevesearch/bleve/v2/analysis/analyzer/keyword"
-	"github.com/blevesearch/bleve/v2/analysis/token/lowercase"
-	"github.com/blevesearch/bleve/v2/analysis/tokenizer/single"
-	"github.com/blevesearch/bleve/v2/mapping"
-	"github.com/blevesearch/bleve/v2/search"
-	"github.com/cs3org/reva/v2/pkg/storagespace"
-	"google.golang.org/protobuf/types/known/timestamppb"
-
-	sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
-	"github.com/cs3org/reva/v2/pkg/utils"
-	searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
-	searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
-)
-
-type indexDocument struct {
-	RootID string
-	Path   string
-	ID     string
-
-	Name     string
-	Size     uint64
-	Mtime    string
-	MimeType string
-	Type     uint64
-
-	Deleted bool
-}
-
-// Index represents a bleve based search index
-type Index struct {
-	bleveIndex bleve.Index
-}
-
-// NewPersisted returns a new instance of Index with the data being persisted in the given directory
-func NewPersisted(path string) (*Index, error) {
-	mapping, err := BuildMapping()
-	if err != nil {
-		return nil, err
-	}
-	bi, err := bleve.New(path, mapping)
-	if err != nil {
-		return nil, err
-	}
-	return New(bi)
-}
-
-// New returns a new instance of Index using the given bleve Index as the backend
-func New(bleveIndex bleve.Index) (*Index, error) {
-	return &Index{
-		bleveIndex: bleveIndex,
-	}, nil
-}
-
-// DocCount returns the number of elemenst in the index
-func (i *Index) DocCount() (uint64, error) {
-	return i.bleveIndex.DocCount()
-}
-
-// Add adds a new entity to the Index
-func (i *Index) Add(ref *sprovider.Reference, ri *sprovider.ResourceInfo) error {
-	entity := toEntity(ref, ri)
-	return i.bleveIndex.Index(idToBleveId(ri.Id), entity)
-}
-
-// Delete marks an entity from the index as deleten (still keeping it around)
-func (i *Index) Delete(id *sprovider.ResourceId) error {
-	return i.markAsDeleted(idToBleveId(id), true)
-}
-
-// Restore marks an entity from the index as not being deleted
-func (i *Index) Restore(id *sprovider.ResourceId) error {
-	return i.markAsDeleted(idToBleveId(id), false)
-}
-
-func (i *Index) markAsDeleted(id string, deleted bool) error {
-	doc, err := i.updateEntity(id, func(doc *indexDocument) {
-		doc.Deleted = deleted
-	})
-	if err != nil {
-		return err
-	}
-
-	if doc.Type == uint64(sprovider.ResourceType_RESOURCE_TYPE_CONTAINER) {
-		query := bleve.NewConjunctionQuery(
-			bleve.NewQueryStringQuery("RootID:"+doc.RootID),
-			bleve.NewQueryStringQuery("Path:"+queryEscape(doc.Path+"/*")),
-		)
-		bleveReq := bleve.NewSearchRequest(query)
-		bleveReq.Size = math.MaxInt
-		bleveReq.Fields = []string{"*"}
-		res, err := i.bleveIndex.Search(bleveReq)
-		if err != nil {
-			return err
-		}
-
-		for _, h := range res.Hits {
-			_, err := i.updateEntity(h.ID, func(doc *indexDocument) {
-				doc.Deleted = deleted
-			})
-			if err != nil {
-				return err
-			}
-		}
-	}
-
-	return nil
-}
-
-func (i *Index) updateEntity(id string, mutateFunc func(doc *indexDocument)) (*indexDocument, error) {
-	doc, err := i.getEntity(id)
-	if err != nil {
-		return nil, err
-	}
-	mutateFunc(doc)
-	err = i.bleveIndex.Index(doc.ID, doc)
-	if err != nil {
-		return nil, err
-	}
-
-	return doc, nil
-}
-
-func (i *Index) getEntity(id string) (*indexDocument, error) {
-	req := bleve.NewSearchRequest(bleve.NewDocIDQuery([]string{id}))
-	req.Fields = []string{"*"}
-	res, err := i.bleveIndex.Search(req)
-	if err != nil {
-		return nil, err
-	}
-	if res.Hits.Len() == 0 {
-		return nil, errors.New("entity not found")
-	}
-	return fieldsToEntity(res.Hits[0].Fields), nil
-}
-
-// Purge removes an entity from the index
-func (i *Index) Purge(id *sprovider.ResourceId) error {
-	return i.bleveIndex.Delete(idToBleveId(id))
-}
-
-// Move update the path of an entry and all its children
-func (i *Index) Move(id *sprovider.ResourceId, fullPath string) error {
-	bleveId := idToBleveId(id)
-	doc, err := i.getEntity(bleveId)
-	if err != nil {
-		return err
-	}
-	oldName := doc.Path
-	newName := utils.MakeRelativePath(fullPath)
-
-	doc, err = i.updateEntity(bleveId, func(doc *indexDocument) {
-		doc.Path = newName
-		doc.Name = path.Base(newName)
-	})
-	if err != nil {
-		return err
-	}
-
-	if doc.Type == uint64(sprovider.ResourceType_RESOURCE_TYPE_CONTAINER) {
-		query := bleve.NewConjunctionQuery(
-			bleve.NewQueryStringQuery("RootID:"+doc.RootID),
-			bleve.NewQueryStringQuery("Path:"+queryEscape(oldName+"/*")),
-		)
-		bleveReq := bleve.NewSearchRequest(query)
-		bleveReq.Size = math.MaxInt
-		bleveReq.Fields = []string{"*"}
-		res, err := i.bleveIndex.Search(bleveReq)
-		if err != nil {
-			return err
-		}
-
-		for _, h := range res.Hits {
-			_, err := i.updateEntity(h.ID, func(doc *indexDocument) {
-				doc.Path = strings.Replace(doc.Path, oldName, newName, 1)
-			})
-			if err != nil {
-				return err
-			}
-		}
-	}
-
-	return nil
-}
-
-// Search searches the index according to the criteria specified in the given SearchIndexRequest
-func (i *Index) Search(ctx context.Context, req *searchsvc.SearchIndexRequest) (*searchsvc.SearchIndexResponse, error) {
-	deletedQuery := bleve.NewBoolFieldQuery(false)
-	deletedQuery.SetField("Deleted")
-	query := bleve.NewConjunctionQuery(
-		bleve.NewQueryStringQuery(req.Query),
-		deletedQuery, // Skip documents that have been marked as deleted
-		bleve.NewQueryStringQuery("RootID:"+idToBleveId(&sprovider.ResourceId{
-			StorageId: req.Ref.GetResourceId().GetStorageId(),
-			SpaceId:   req.Ref.GetResourceId().GetSpaceId(),
-			OpaqueId:  req.Ref.GetResourceId().GetOpaqueId(),
-		})), // Limit search to the space
-		bleve.NewQueryStringQuery("Path:"+queryEscape(utils.MakeRelativePath(path.Join(req.Ref.Path, "/"))+"*")), // Limit search to this directory in the space
-	)
-	bleveReq := bleve.NewSearchRequest(query)
-	bleveReq.Size = 200
-	if req.PageSize > 0 {
-		bleveReq.Size = int(req.PageSize)
-	}
-	bleveReq.Fields = []string{"*"}
-	res, err := i.bleveIndex.Search(bleveReq)
-	if err != nil {
-		return nil, err
-	}
-
-	matches := []*searchmsg.Match{}
-	for _, h := range res.Hits {
-		match, err := fromDocumentMatch(h)
-		if err != nil {
-			return nil, err
-		}
-		matches = append(matches, match)
-	}
-
-	return &searchsvc.SearchIndexResponse{
-		Matches:      matches,
-		TotalMatches: int32(res.Total),
-	}, nil
-}
-
-// BuildMapping builds a bleve index mapping which can be used for indexing
-func BuildMapping() (mapping.IndexMapping, error) {
-	nameMapping := bleve.NewTextFieldMapping()
-	nameMapping.Analyzer = "lowercaseKeyword"
-
-	docMapping := bleve.NewDocumentMapping()
-	docMapping.AddFieldMappingsAt("Name", nameMapping)
-
-	indexMapping := bleve.NewIndexMapping()
-	indexMapping.DefaultAnalyzer = keyword.Name
-	indexMapping.DefaultMapping = docMapping
-	err := indexMapping.AddCustomAnalyzer("lowercaseKeyword",
-		map[string]interface{}{
-			"type":      custom.Name,
-			"tokenizer": single.Name,
-			"token_filters": []string{
-				lowercase.Name,
-			},
-		})
-	if err != nil {
-		return nil, err
-	}
-
-	return indexMapping, nil
-}
-
-func toEntity(ref *sprovider.Reference, ri *sprovider.ResourceInfo) *indexDocument {
-	doc := &indexDocument{
-		RootID:   idToBleveId(ref.ResourceId),
-		Path:     ref.Path,
-		ID:       idToBleveId(ri.Id),
-		Name:     ri.Path,
-		Size:     ri.Size,
-		MimeType: ri.MimeType,
-		Type:     uint64(ri.Type),
-		Deleted:  false,
-	}
-
-	if ri.Mtime != nil {
-		doc.Mtime = time.Unix(int64(ri.Mtime.Seconds), int64(ri.Mtime.Nanos)).UTC().Format(time.RFC3339)
-	}
-
-	return doc
-}
-
-func fieldsToEntity(fields map[string]interface{}) *indexDocument {
-	doc := &indexDocument{
-		RootID:   fields["RootID"].(string),
-		Path:     fields["Path"].(string),
-		ID:       fields["ID"].(string),
-		Name:     fields["Name"].(string),
-		Size:     uint64(fields["Size"].(float64)),
-		Mtime:    fields["Mtime"].(string),
-		MimeType: fields["MimeType"].(string),
-		Type:     uint64(fields["Type"].(float64)),
-		Deleted:  fields["Deleted"].(bool),
-	}
-	return doc
-}
-
-func fromDocumentMatch(hit *search.DocumentMatch) (*searchmsg.Match, error) {
-	rootID, err := storagespace.ParseID(hit.Fields["RootID"].(string))
-	if err != nil {
-		return nil, err
-	}
-	rID, err := storagespace.ParseID(hit.Fields["ID"].(string))
-	if err != nil {
-		return nil, err
-	}
-
-	match := &searchmsg.Match{
-		Score: float32(hit.Score),
-		Entity: &searchmsg.Entity{
-			Ref: &searchmsg.Reference{
-				ResourceId: resourceIDtoSearchID(rootID),
-				Path:       hit.Fields["Path"].(string),
-			},
-			Id:       resourceIDtoSearchID(rID),
-			Name:     hit.Fields["Name"].(string),
-			Size:     uint64(hit.Fields["Size"].(float64)),
-			Type:     uint64(hit.Fields["Type"].(float64)),
-			MimeType: hit.Fields["MimeType"].(string),
-			Deleted:  hit.Fields["Deleted"].(bool),
-		},
-	}
-
-	if mtime, err := time.Parse(time.RFC3339, hit.Fields["Mtime"].(string)); err == nil {
-		match.Entity.LastModifiedTime = &timestamppb.Timestamp{Seconds: mtime.Unix(), Nanos: int32(mtime.Nanosecond())}
-	}
-
-	return match, nil
-}
-
-func idToBleveId(id *sprovider.ResourceId) string {
-	if id == nil {
-		return ""
-	}
-	return storagespace.FormatResourceID(*id)
-}
-
-func resourceIDtoSearchID(id sprovider.ResourceId) *searchmsg.ResourceID {
-	return &searchmsg.ResourceID{
-		StorageId: id.GetStorageId(),
-		SpaceId:   id.GetSpaceId(),
-		OpaqueId:  id.GetOpaqueId()}
-}
-
-func queryEscape(s string) string {
-	re := regexp.MustCompile(`([` + regexp.QuoteMeta(`+=&|><!(){}[]^\"~*?:\/`) + `\-\s])`)
-	return re.ReplaceAllString(s, "\\$1")
-}
diff --git a/services/search/pkg/search/index/mocks/BleveIndex.go b/services/search/pkg/search/index/mocks/BleveIndex.go
deleted file mode 100644
index 1bb4f1b28d3..00000000000
--- a/services/search/pkg/search/index/mocks/BleveIndex.go
+++ /dev/null
@@ -1,415 +0,0 @@
-// Code generated by mockery v2.10.0. DO NOT EDIT.
-
-package mocks
-
-import (
-	context "context"
-
-	bleve "github.com/blevesearch/bleve/v2"
-
-	index "github.com/blevesearch/bleve_index_api"
-
-	mapping "github.com/blevesearch/bleve/v2/mapping"
-
-	mock "github.com/stretchr/testify/mock"
-)
-
-// BleveIndex is an autogenerated mock type for the BleveIndex type
-type BleveIndex struct {
-	mock.Mock
-}
-
-// Advanced provides a mock function with given fields:
-func (_m *BleveIndex) Advanced() (index.Index, error) {
-	ret := _m.Called()
-
-	var r0 index.Index
-	if rf, ok := ret.Get(0).(func() index.Index); ok {
-		r0 = rf()
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(index.Index)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func() error); ok {
-		r1 = rf()
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// Batch provides a mock function with given fields: b
-func (_m *BleveIndex) Batch(b *bleve.Batch) error {
-	ret := _m.Called(b)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(*bleve.Batch) error); ok {
-		r0 = rf(b)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
-// Close provides a mock function with given fields:
-func (_m *BleveIndex) Close() error {
-	ret := _m.Called()
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func() error); ok {
-		r0 = rf()
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
-// Delete provides a mock function with given fields: id
-func (_m *BleveIndex) Delete(id string) error {
-	ret := _m.Called(id)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(string) error); ok {
-		r0 = rf(id)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
-// DeleteInternal provides a mock function with given fields: key
-func (_m *BleveIndex) DeleteInternal(key []byte) error {
-	ret := _m.Called(key)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func([]byte) error); ok {
-		r0 = rf(key)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
-// DocCount provides a mock function with given fields:
-func (_m *BleveIndex) DocCount() (uint64, error) {
-	ret := _m.Called()
-
-	var r0 uint64
-	if rf, ok := ret.Get(0).(func() uint64); ok {
-		r0 = rf()
-	} else {
-		r0 = ret.Get(0).(uint64)
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func() error); ok {
-		r1 = rf()
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// Document provides a mock function with given fields: id
-func (_m *BleveIndex) Document(id string) (index.Document, error) {
-	ret := _m.Called(id)
-
-	var r0 index.Document
-	if rf, ok := ret.Get(0).(func(string) index.Document); ok {
-		r0 = rf(id)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(index.Document)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(string) error); ok {
-		r1 = rf(id)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// FieldDict provides a mock function with given fields: field
-func (_m *BleveIndex) FieldDict(field string) (index.FieldDict, error) {
-	ret := _m.Called(field)
-
-	var r0 index.FieldDict
-	if rf, ok := ret.Get(0).(func(string) index.FieldDict); ok {
-		r0 = rf(field)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(index.FieldDict)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(string) error); ok {
-		r1 = rf(field)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// FieldDictPrefix provides a mock function with given fields: field, termPrefix
-func (_m *BleveIndex) FieldDictPrefix(field string, termPrefix []byte) (index.FieldDict, error) {
-	ret := _m.Called(field, termPrefix)
-
-	var r0 index.FieldDict
-	if rf, ok := ret.Get(0).(func(string, []byte) index.FieldDict); ok {
-		r0 = rf(field, termPrefix)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(index.FieldDict)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(string, []byte) error); ok {
-		r1 = rf(field, termPrefix)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// FieldDictRange provides a mock function with given fields: field, startTerm, endTerm
-func (_m *BleveIndex) FieldDictRange(field string, startTerm []byte, endTerm []byte) (index.FieldDict, error) {
-	ret := _m.Called(field, startTerm, endTerm)
-
-	var r0 index.FieldDict
-	if rf, ok := ret.Get(0).(func(string, []byte, []byte) index.FieldDict); ok {
-		r0 = rf(field, startTerm, endTerm)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(index.FieldDict)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(string, []byte, []byte) error); ok {
-		r1 = rf(field, startTerm, endTerm)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// Fields provides a mock function with given fields:
-func (_m *BleveIndex) Fields() ([]string, error) {
-	ret := _m.Called()
-
-	var r0 []string
-	if rf, ok := ret.Get(0).(func() []string); ok {
-		r0 = rf()
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func() error); ok {
-		r1 = rf()
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// GetInternal provides a mock function with given fields: key
-func (_m *BleveIndex) GetInternal(key []byte) ([]byte, error) {
-	ret := _m.Called(key)
-
-	var r0 []byte
-	if rf, ok := ret.Get(0).(func([]byte) []byte); ok {
-		r0 = rf(key)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]byte)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func([]byte) error); ok {
-		r1 = rf(key)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// Index provides a mock function with given fields: id, data
-func (_m *BleveIndex) Index(id string, data interface{}) error {
-	ret := _m.Called(id, data)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(string, interface{}) error); ok {
-		r0 = rf(id, data)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
-// Mapping provides a mock function with given fields:
-func (_m *BleveIndex) Mapping() mapping.IndexMapping {
-	ret := _m.Called()
-
-	var r0 mapping.IndexMapping
-	if rf, ok := ret.Get(0).(func() mapping.IndexMapping); ok {
-		r0 = rf()
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(mapping.IndexMapping)
-		}
-	}
-
-	return r0
-}
-
-// Name provides a mock function with given fields:
-func (_m *BleveIndex) Name() string {
-	ret := _m.Called()
-
-	var r0 string
-	if rf, ok := ret.Get(0).(func() string); ok {
-		r0 = rf()
-	} else {
-		r0 = ret.Get(0).(string)
-	}
-
-	return r0
-}
-
-// NewBatch provides a mock function with given fields:
-func (_m *BleveIndex) NewBatch() *bleve.Batch {
-	ret := _m.Called()
-
-	var r0 *bleve.Batch
-	if rf, ok := ret.Get(0).(func() *bleve.Batch); ok {
-		r0 = rf()
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*bleve.Batch)
-		}
-	}
-
-	return r0
-}
-
-// Search provides a mock function with given fields: req
-func (_m *BleveIndex) Search(req *bleve.SearchRequest) (*bleve.SearchResult, error) {
-	ret := _m.Called(req)
-
-	var r0 *bleve.SearchResult
-	if rf, ok := ret.Get(0).(func(*bleve.SearchRequest) *bleve.SearchResult); ok {
-		r0 = rf(req)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*bleve.SearchResult)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(*bleve.SearchRequest) error); ok {
-		r1 = rf(req)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// SearchInContext provides a mock function with given fields: ctx, req
-func (_m *BleveIndex) SearchInContext(ctx context.Context, req *bleve.SearchRequest) (*bleve.SearchResult, error) {
-	ret := _m.Called(ctx, req)
-
-	var r0 *bleve.SearchResult
-	if rf, ok := ret.Get(0).(func(context.Context, *bleve.SearchRequest) *bleve.SearchResult); ok {
-		r0 = rf(ctx, req)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*bleve.SearchResult)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, *bleve.SearchRequest) error); ok {
-		r1 = rf(ctx, req)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// SetInternal provides a mock function with given fields: key, val
-func (_m *BleveIndex) SetInternal(key []byte, val []byte) error {
-	ret := _m.Called(key, val)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func([]byte, []byte) error); ok {
-		r0 = rf(key, val)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
-// SetName provides a mock function with given fields: _a0
-func (_m *BleveIndex) SetName(_a0 string) {
-	_m.Called(_a0)
-}
-
-// Stats provides a mock function with given fields:
-func (_m *BleveIndex) Stats() *bleve.IndexStat {
-	ret := _m.Called()
-
-	var r0 *bleve.IndexStat
-	if rf, ok := ret.Get(0).(func() *bleve.IndexStat); ok {
-		r0 = rf()
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*bleve.IndexStat)
-		}
-	}
-
-	return r0
-}
-
-// StatsMap provides a mock function with given fields:
-func (_m *BleveIndex) StatsMap() map[string]interface{} {
-	ret := _m.Called()
-
-	var r0 map[string]interface{}
-	if rf, ok := ret.Get(0).(func() map[string]interface{}); ok {
-		r0 = rf()
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(map[string]interface{})
-		}
-	}
-
-	return r0
-}
diff --git a/services/search/pkg/search/mocks/ProviderClient.go b/services/search/pkg/search/mocks/ProviderClient.go
deleted file mode 100644
index 7435e78cba3..00000000000
--- a/services/search/pkg/search/mocks/ProviderClient.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// Code generated by mockery v2.10.0. DO NOT EDIT.
-
-package mocks
-
-import (
-	context "context"
-
-	mock "github.com/stretchr/testify/mock"
-
-	v0 "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
-)
-
-// ProviderClient is an autogenerated mock type for the ProviderClient type
-type ProviderClient struct {
-	mock.Mock
-}
-
-// IndexSpace provides a mock function with given fields: ctx, req
-func (_m *ProviderClient) IndexSpace(ctx context.Context, req *v0.IndexSpaceRequest) (*v0.IndexSpaceResponse, error) {
-	ret := _m.Called(ctx, req)
-
-	var r0 *v0.IndexSpaceResponse
-	if rf, ok := ret.Get(0).(func(context.Context, *v0.IndexSpaceRequest) *v0.IndexSpaceResponse); ok {
-		r0 = rf(ctx, req)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*v0.IndexSpaceResponse)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, *v0.IndexSpaceRequest) error); ok {
-		r1 = rf(ctx, req)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// Search provides a mock function with given fields: ctx, req
-func (_m *ProviderClient) Search(ctx context.Context, req *v0.SearchRequest) (*v0.SearchResponse, error) {
-	ret := _m.Called(ctx, req)
-
-	var r0 *v0.SearchResponse
-	if rf, ok := ret.Get(0).(func(context.Context, *v0.SearchRequest) *v0.SearchResponse); ok {
-		r0 = rf(ctx, req)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*v0.SearchResponse)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, *v0.SearchRequest) error); ok {
-		r1 = rf(ctx, req)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
diff --git a/services/search/pkg/search/provider/searchprovider.go b/services/search/pkg/search/provider.go
similarity index 70%
rename from services/search/pkg/search/provider/searchprovider.go
rename to services/search/pkg/search/provider.go
index 719fa6a7b99..4c35fe7ad8a 100644
--- a/services/search/pkg/search/provider/searchprovider.go
+++ b/services/search/pkg/search/provider.go
@@ -1,13 +1,8 @@
-package provider
+package search
 
 import (
 	"context"
 	"fmt"
-	"path/filepath"
-	"sort"
-	"strings"
-	"time"
-
 	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
 	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
 	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
@@ -15,76 +10,51 @@ import (
 	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
 	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
 	"github.com/cs3org/reva/v2/pkg/errtypes"
-	"github.com/cs3org/reva/v2/pkg/events"
 	sdk "github.com/cs3org/reva/v2/pkg/sdk/common"
 	"github.com/cs3org/reva/v2/pkg/storage/utils/walker"
 	"github.com/cs3org/reva/v2/pkg/storagespace"
 	"github.com/cs3org/reva/v2/pkg/utils"
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
-	"github.com/owncloud/ocis/v2/services/search/pkg/search"
+	"github.com/owncloud/ocis/v2/services/search/pkg/content"
+	"github.com/owncloud/ocis/v2/services/search/pkg/engine"
 	"google.golang.org/grpc/metadata"
+	"path/filepath"
+	"sort"
+	"strings"
 
 	searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
 	searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
 )
 
-var ListenEvents = []events.Unmarshaller{
-	events.ItemTrashed{},
-	events.ItemRestored{},
-	events.ItemMoved{},
-	events.ContainerCreated{},
-	events.FileUploaded{},
-	events.FileTouched{},
-	events.FileVersionRestored{},
-}
-
+// Provider is responsible for indexing spaces and pass on a search
+// to it's underlying engine.
 type Provider struct {
-	logger            log.Logger
-	gwClient          gateway.GatewayAPIClient
-	indexClient       search.IndexClient
-	machineAuthAPIKey string
+	logger    log.Logger
+	gateway   gateway.GatewayAPIClient
+	engine    engine.Engine
+	extractor content.Extractor
+	secret    string
 }
 
-type MatchArray []*searchmsg.Match
-
-func (s MatchArray) Len() int {
-	return len(s)
-}
-func (s MatchArray) Swap(i, j int) {
-	s[i], s[j] = s[j], s[i]
-}
-func (s MatchArray) Less(i, j int) bool {
-	return s[i].Score > s[j].Score
-}
-
-func New(gwClient gateway.GatewayAPIClient, indexClient search.IndexClient, machineAuthAPIKey string, eventsChan <-chan interface{}, logger log.Logger) *Provider {
-	p := &Provider{
-		gwClient:          gwClient,
-		indexClient:       indexClient,
-		machineAuthAPIKey: machineAuthAPIKey,
-		logger:            logger,
+// NewProvider creates a new Provider instance.
+func NewProvider(gw gateway.GatewayAPIClient, eng engine.Engine, extractor content.Extractor, logger log.Logger, secret string) *Provider {
+	return &Provider{
+		gateway:   gw,
+		engine:    eng,
+		secret:    secret,
+		logger:    logger,
+		extractor: extractor,
 	}
-
-	go func() {
-		for {
-			ev := <-eventsChan
-			go func() {
-				time.Sleep(1 * time.Second) // Give some time to let everything settle down before trying to access it when indexing
-				p.handleEvent(ev)
-			}()
-		}
-	}()
-
-	return p
 }
 
+// Search processes a search request and passes it down to the engine.
 func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*searchsvc.SearchResponse, error) {
 	if req.Query == "" {
 		return nil, errtypes.BadRequest("empty query provided")
 	}
 	p.logger.Debug().Str("query", req.Query).Msg("performing a search")
 
-	listSpacesRes, err := p.gwClient.ListStorageSpaces(ctx, &provider.ListStorageSpacesRequest{
+	listSpacesRes, err := p.gateway.ListStorageSpaces(ctx, &provider.ListStorageSpacesRequest{
 		Filters: []*provider.ListStorageSpacesRequest_Filter{
 			{
 				Type: provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE,
@@ -111,7 +81,7 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s
 		mountpointMap[grantSpaceId] = space.Id.OpaqueId
 	}
 
-	matches := MatchArray{}
+	matches := matchArray{}
 	total := int32(0)
 	for _, space := range listSpacesRes.StorageSpaces {
 		searchRootId := &searchmsg.ResourceID{
@@ -132,7 +102,7 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s
 				p.logger.Warn().Interface("space", space).Msg("could not find mountpoint space for grant space")
 				continue
 			}
-			gpRes, err := p.gwClient.GetPath(ctx, &provider.GetPathRequest{
+			gpRes, err := p.gateway.GetPath(ctx, &provider.GetPathRequest{
 				ResourceId: space.Root,
 			})
 			if err != nil {
@@ -157,8 +127,8 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s
 			p.logger.Debug().Interface("grantSpace", space).Interface("mountpointRootId", mountpointRootId).Msg("searching a grant")
 		}
 
-		res, err := p.indexClient.Search(ctx, &searchsvc.SearchIndexRequest{
-			Query: formatQuery(req.Query),
+		res, err := p.engine.Search(ctx, &searchsvc.SearchIndexRequest{
+			Query: req.Query,
 			Ref: &searchmsg.Reference{
 				ResourceId: searchRootId,
 				Path:       mountpointPrefix,
@@ -199,9 +169,10 @@ func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*s
 	}, nil
 }
 
+// IndexSpace (re)indexes all resources of a given space.
 func (p *Provider) IndexSpace(ctx context.Context, req *searchsvc.IndexSpaceRequest) (*searchsvc.IndexSpaceResponse, error) {
 	// get user
-	res, err := p.gwClient.GetUserByClaim(context.Background(), &user.GetUserByClaimRequest{
+	res, err := p.gateway.GetUserByClaim(context.Background(), &user.GetUserByClaimRequest{
 		Claim: "username",
 		Value: req.UserId,
 	})
@@ -212,10 +183,10 @@ func (p *Provider) IndexSpace(ctx context.Context, req *searchsvc.IndexSpaceRequ
 
 	// Get auth context
 	ownerCtx := ctxpkg.ContextSetUser(context.Background(), res.User)
-	authRes, err := p.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{
+	authRes, err := p.gateway.Authenticate(ownerCtx, &gateway.AuthenticateRequest{
 		Type:         "machine",
 		ClientId:     "userid:" + res.User.Id.OpaqueId,
-		ClientSecret: p.machineAuthAPIKey,
+		ClientSecret: p.secret,
 	})
 	if err != nil || authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
 		return nil, err
@@ -227,13 +198,13 @@ func (p *Provider) IndexSpace(ctx context.Context, req *searchsvc.IndexSpaceRequ
 	ownerCtx = metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token)
 
 	// Walk the space and index all files
-	walker := walker.NewWalker(p.gwClient)
+	w := walker.NewWalker(p.gateway)
 	rootId, err := storagespace.ParseID(req.SpaceId)
 	if err != nil {
 		p.logger.Error().Err(err).Msg(err.Error())
 		return nil, err
 	}
-	err = walker.Walk(ownerCtx, &rootId, func(wd string, info *provider.ResourceInfo, err error) error {
+	err = w.Walk(ownerCtx, &rootId, func(wd string, info *provider.ResourceInfo, err error) error {
 		if err != nil {
 			p.logger.Error().Err(err).Msg("error walking the tree")
 		}
@@ -241,7 +212,21 @@ func (p *Provider) IndexSpace(ctx context.Context, req *searchsvc.IndexSpaceRequ
 			Path:       utils.MakeRelativePath(filepath.Join(wd, info.Path)),
 			ResourceId: &rootId,
 		}
-		err = p.indexClient.Add(ref, info)
+
+		doc, err := p.extractor.Extract(ctx, ref, info)
+		if err != nil {
+			p.logger.Error().Err(err).Msg("error extracting content")
+		}
+
+		r := engine.Resource{
+			ID:       storagespace.FormatResourceID(*info.Id),
+			RootID:   storagespace.FormatResourceID(*ref.ResourceId),
+			Path:     ref.Path,
+			Type:     uint64(info.Type),
+			Document: doc,
+		}
+
+		err = p.engine.Upsert(r.ID, r)
 		if err != nil {
 			p.logger.Error().Err(err).Msg("error adding resource to the index")
 		} else {
@@ -253,29 +238,6 @@ func (p *Provider) IndexSpace(ctx context.Context, req *searchsvc.IndexSpaceRequ
 		return nil, err
 	}
 
-	p.logDocCount()
+	logDocCount(p.engine, p.logger)
 	return &searchsvc.IndexSpaceResponse{}, nil
 }
-
-func (p *Provider) logDocCount() {
-	c, err := p.indexClient.DocCount()
-	if err != nil {
-		p.logger.Error().Err(err).Msg("error getting document count from the index")
-	}
-	p.logger.Debug().Interface("count", c).Msg("new document count")
-}
-
-func formatQuery(q string) string {
-	query := q
-	fields := []string{"RootID", "Path", "ID", "Name", "Size", "Mtime", "MimeType", "Type"}
-	for _, field := range fields {
-		query = strings.ReplaceAll(query, strings.ToLower(field)+":", field+":")
-	}
-
-	if strings.Contains(query, ":") {
-		return query // Sophisticated field based search
-	}
-
-	// this is a basic filename search
-	return "Name:*" + strings.ReplaceAll(strings.ToLower(query), " ", `\ `) + "*"
-}
diff --git a/services/search/pkg/search/provider/events.go b/services/search/pkg/search/provider/events.go
deleted file mode 100644
index b68a13a418a..00000000000
--- a/services/search/pkg/search/provider/events.go
+++ /dev/null
@@ -1,161 +0,0 @@
-package provider
-
-import (
-	"context"
-
-	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
-	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
-	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
-	rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
-	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
-	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
-	"github.com/cs3org/reva/v2/pkg/errtypes"
-	"github.com/cs3org/reva/v2/pkg/events"
-	"google.golang.org/grpc/metadata"
-)
-
-func (p *Provider) handleEvent(ev interface{}) {
-	var ref *provider.Reference
-	var owner *user.User
-	switch e := ev.(type) {
-	case events.ItemTrashed:
-		p.logger.Debug().Interface("event", ev).Msg("marking document as deleted")
-		err := p.indexClient.Delete(e.ID)
-		if err != nil {
-			p.logger.Error().Err(err).Interface("Id", e.ID).Msg("failed to remove item from index")
-		}
-		return
-	case events.ItemRestored:
-		p.logger.Debug().Interface("event", ev).Msg("marking document as restored")
-		ref = e.Ref
-		owner = &user.User{
-			Id: e.Executant,
-		}
-
-		statRes, err := p.statResource(ref, owner)
-		if err != nil {
-			p.logger.Error().Err(err).Msg("failed to stat the changed resource")
-			return
-		}
-
-		switch statRes.Status.Code {
-		case rpc.Code_CODE_OK:
-			err = p.indexClient.Restore(statRes.Info.Id)
-			if err != nil {
-				p.logger.Error().Err(err).Msg("failed to restore the changed resource in the index")
-			}
-		default:
-			p.logger.Error().Interface("statRes", statRes).Msg("failed to stat the changed resource")
-		}
-
-		return
-	case events.ItemMoved:
-		p.logger.Debug().Interface("event", ev).Msg("resource has been moved, updating the document")
-		ref = e.Ref
-		owner = &user.User{
-			Id: e.Executant,
-		}
-
-		statRes, err := p.statResource(ref, owner)
-		if err != nil {
-			p.logger.Error().Err(err).Msg("failed to stat the moved resource")
-			return
-		}
-		if statRes.Status.Code != rpc.Code_CODE_OK {
-			p.logger.Error().Interface("statRes", statRes).Msg("failed to stat the moved resource")
-			return
-		}
-
-		gpRes, err := p.getPath(statRes.Info.Id, owner)
-		if err != nil {
-			p.logger.Error().Err(err).Interface("ref", ref).Msg("failed to get path for moved resource")
-			return
-		}
-		if gpRes.Status.Code != rpcv1beta1.Code_CODE_OK {
-			p.logger.Error().Interface("status", gpRes.Status).Interface("ref", ref).Msg("failed to get path for moved resource")
-			return
-		}
-
-		err = p.indexClient.Move(statRes.Info.Id, gpRes.Path)
-		if err != nil {
-			p.logger.Error().Err(err).Msg("failed to move the changed resource in the index")
-		}
-		return
-	case events.ContainerCreated:
-		ref = e.Ref
-		owner = &user.User{
-			Id: e.Executant,
-		}
-	case events.FileUploaded:
-		ref = e.Ref
-		owner = &user.User{
-			Id: e.Executant,
-		}
-	case events.FileTouched:
-		ref = e.Ref
-		owner = &user.User{
-			Id: e.Executant,
-		}
-	case events.FileVersionRestored:
-		ref = e.Ref
-		owner = &user.User{
-			Id: e.Executant,
-		}
-	default:
-		// Not sure what to do here. Skip.
-		return
-	}
-	p.logger.Debug().Interface("event", ev).Msg("resource has been changed, updating the document")
-
-	statRes, err := p.statResource(ref, owner)
-	if err != nil {
-		p.logger.Error().Err(err).Msg("failed to stat the changed resource")
-		return
-	}
-	if statRes.Status.Code != rpc.Code_CODE_OK {
-		p.logger.Error().Interface("statRes", statRes).Msg("failed to stat the changed resource")
-		return
-	}
-
-	err = p.indexClient.Add(ref, statRes.Info)
-	if err != nil {
-		p.logger.Error().Err(err).Msg("error adding updating the resource in the index")
-	} else {
-		p.logDocCount()
-	}
-}
-
-func (p *Provider) statResource(ref *provider.Reference, owner *user.User) (*provider.StatResponse, error) {
-	ownerCtx, err := p.getAuthContext(owner)
-	if err != nil {
-		return nil, err
-	}
-
-	return p.gwClient.Stat(ownerCtx, &provider.StatRequest{Ref: ref})
-}
-
-func (p *Provider) getPath(id *provider.ResourceId, owner *user.User) (*provider.GetPathResponse, error) {
-	ownerCtx, err := p.getAuthContext(owner)
-	if err != nil {
-		return nil, err
-	}
-
-	return p.gwClient.GetPath(ownerCtx, &provider.GetPathRequest{ResourceId: id})
-}
-
-func (p *Provider) getAuthContext(owner *user.User) (context.Context, error) {
-	ownerCtx := ctxpkg.ContextSetUser(context.Background(), owner)
-	authRes, err := p.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{
-		Type:         "machine",
-		ClientId:     "userid:" + owner.GetId().GetOpaqueId(),
-		ClientSecret: p.machineAuthAPIKey,
-	})
-	if err == nil && authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
-		err = errtypes.NewErrtypeFromStatus(authRes.Status)
-	}
-	if err != nil {
-		p.logger.Error().Err(err).Interface("owner", owner).Interface("authRes", authRes).Msg("error using machine auth")
-		return nil, err
-	}
-	return metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token), nil
-}
diff --git a/services/search/pkg/search/search.go b/services/search/pkg/search/search.go
index 0a8bedd2662..a204e8ac722 100644
--- a/services/search/pkg/search/search.go
+++ b/services/search/pkg/search/search.go
@@ -1,46 +1,73 @@
-// Copyright 2018-2022 CERN
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// In applying this license, CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
 package search
 
 import (
 	"context"
-
-	providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
-	searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
+	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
+	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
+	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
+	"github.com/cs3org/reva/v2/pkg/errtypes"
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
+	"github.com/owncloud/ocis/v2/services/search/pkg/engine"
+	"google.golang.org/grpc/metadata"
 )
 
-//go:generate mockery --name=ProviderClient
-//go:generate mockery --name=IndexClient
+type matchArray []*searchmsg.Match
+
+func (ma matchArray) Len() int {
+	return len(ma)
+}
+func (ma matchArray) Swap(i, j int) {
+	ma[i], ma[j] = ma[j], ma[i]
+}
+func (ma matchArray) Less(i, j int) bool {
+	return ma[i].Score > ma[j].Score
+}
 
-// ProviderClient is the interface to the search provider service
-type ProviderClient interface {
-	Search(ctx context.Context, req *searchsvc.SearchRequest) (*searchsvc.SearchResponse, error)
-	IndexSpace(ctx context.Context, req *searchsvc.IndexSpaceRequest) (*searchsvc.IndexSpaceResponse, error)
+func logDocCount(engine engine.Engine, logger log.Logger) {
+	c, err := engine.DocCount()
+	if err != nil {
+		logger.Error().Err(err).Msg("error getting document count from the index")
+	}
+	logger.Debug().Interface("count", c).Msg("new document count")
 }
 
-// IndexClient is the interface to the search index
-type IndexClient interface {
-	Search(ctx context.Context, req *searchsvc.SearchIndexRequest) (*searchsvc.SearchIndexResponse, error)
-	Add(ref *providerv1beta1.Reference, ri *providerv1beta1.ResourceInfo) error
-	Move(id *providerv1beta1.ResourceId, fullPath string) error
-	Delete(id *providerv1beta1.ResourceId) error
-	Restore(id *providerv1beta1.ResourceId) error
-	Purge(id *providerv1beta1.ResourceId) error
-	DocCount() (uint64, error)
+func getAuthContext(owner *user.User, gw gateway.GatewayAPIClient, secret string, logger log.Logger) (context.Context, error) {
+	ownerCtx := ctxpkg.ContextSetUser(context.Background(), owner)
+	authRes, err := gw.Authenticate(ownerCtx, &gateway.AuthenticateRequest{
+		Type:         "machine",
+		ClientId:     "userid:" + owner.GetId().GetOpaqueId(),
+		ClientSecret: secret,
+	})
+
+	if err == nil && authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
+		err = errtypes.NewErrtypeFromStatus(authRes.Status)
+	}
+
+	if err != nil {
+		logger.Error().Err(err).Interface("owner", owner).Interface("authRes", authRes).Msg("error using machine auth")
+		return nil, err
+	}
+
+	return metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token), nil
+}
+
+func statResource(ref *provider.Reference, owner *user.User, gw gateway.GatewayAPIClient, secret string, logger log.Logger) (*provider.StatResponse, error) {
+	ownerCtx, err := getAuthContext(owner, gw, secret, logger)
+	if err != nil {
+		return nil, err
+	}
+
+	return gw.Stat(ownerCtx, &provider.StatRequest{Ref: ref})
+}
+
+func getPath(id *provider.ResourceId, owner *user.User, gw gateway.GatewayAPIClient, secret string, logger log.Logger) (*provider.GetPathResponse, error) {
+	ownerCtx, err := getAuthContext(owner, gw, secret, logger)
+	if err != nil {
+		return nil, err
+	}
+
+	return gw.GetPath(ownerCtx, &provider.GetPathRequest{ResourceId: id})
 }
diff --git a/services/search/pkg/service/v0/service.go b/services/search/pkg/service/v0/service.go
index b46a7111dc2..811e541aae3 100644
--- a/services/search/pkg/service/v0/service.go
+++ b/services/search/pkg/service/v0/service.go
@@ -3,25 +3,21 @@ package service
 import (
 	"context"
 	"errors"
-	"path/filepath"
-
-	"github.com/blevesearch/bleve/v2"
+	"fmt"
 	revactx "github.com/cs3org/reva/v2/pkg/ctx"
 	"github.com/cs3org/reva/v2/pkg/errtypes"
-	"github.com/cs3org/reva/v2/pkg/events"
 	"github.com/cs3org/reva/v2/pkg/events/server"
 	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
 	"github.com/go-micro/plugins/v4/events/natsjs"
+	"github.com/owncloud/ocis/v2/services/search/pkg/content"
+	"github.com/owncloud/ocis/v2/services/search/pkg/engine"
+	"github.com/owncloud/ocis/v2/services/search/pkg/search"
 	merrors "go-micro.dev/v4/errors"
 	"go-micro.dev/v4/metadata"
 	grpcmetadata "google.golang.org/grpc/metadata"
 
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
 	searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
-	"github.com/owncloud/ocis/v2/services/search/pkg/config"
-	"github.com/owncloud/ocis/v2/services/search/pkg/search"
-	"github.com/owncloud/ocis/v2/services/search/pkg/search/index"
-	searchprovider "github.com/owncloud/ocis/v2/services/search/pkg/search/provider"
 )
 
 // NewHandler returns a service implementation for Service.
@@ -30,49 +26,59 @@ func NewHandler(opts ...Option) (searchsvc.SearchProviderHandler, error) {
 	logger := options.Logger
 	cfg := options.Config
 
-	// Connect to nats to listen for changes that need to trigger an index update
-	evtsCfg := cfg.Events
-	client, err := server.NewNatsStream(
-		natsjs.Address(evtsCfg.Endpoint),
-		natsjs.ClusterID(evtsCfg.Cluster),
-	)
-	if err != nil {
-		return nil, err
+	// initialize search engine
+	var eng engine.Engine
+	switch cfg.Engine.Type {
+	case "bleve":
+		idx, err := engine.NewBleveIndex(cfg.Engine.Bleve.Datapath)
+		if err != nil {
+			return nil, err
+		}
+
+		eng = engine.NewBleveEngine(idx)
+	default:
+		return nil, fmt.Errorf("unknown search engine: %s", cfg.Engine.Type)
 	}
-	evts, err := events.Consume(client, evtsCfg.ConsumerGroup, searchprovider.ListenEvents...)
+
+	// initialize gateway
+	gw, err := pool.GetGatewayServiceClient(cfg.Reva.Address)
 	if err != nil {
-		return nil, err
+		logger.Fatal().Err(err).Str("addr", cfg.Reva.Address).Msg("could not get reva client")
 	}
 
-	indexDir := filepath.Join(cfg.Datapath, "index.bleve")
-	bleveIndex, err := bleve.Open(indexDir)
-	if err != nil {
-		mapping, err := index.BuildMapping()
-		if err != nil {
+	// initialize search content extractor
+	var extractor content.Extractor
+	switch cfg.Extractor.Type {
+	case "basic":
+		if extractor, err = content.NewBasicExtractor(logger); err != nil {
 			return nil, err
 		}
-		bleveIndex, err = bleve.New(indexDir, mapping)
-		if err != nil {
+	case "tika":
+		if extractor, err = content.NewTikaExtractor(gw, logger, cfg); err != nil {
 			return nil, err
 		}
+	default:
+		return nil, fmt.Errorf("unknown search extractor: %s", cfg.Extractor.Type)
 	}
-	index, err := index.New(bleveIndex)
+
+	// initialize nats
+	bus, err := server.NewNatsStream(
+		natsjs.Address(cfg.Events.Endpoint),
+		natsjs.ClusterID(cfg.Events.Cluster),
+	)
 	if err != nil {
 		return nil, err
 	}
 
-	gwclient, err := pool.GetGatewayServiceClient(cfg.Reva.Address)
-	if err != nil {
-		logger.Fatal().Err(err).Str("addr", cfg.Reva.Address).Msg("could not get reva client")
+	// setup event handling
+	if err := search.HandleEvents(eng, extractor, gw, bus, logger, cfg); err != nil {
+		return nil, err
 	}
 
-	provider := searchprovider.New(gwclient, index, cfg.MachineAuthAPIKey, evts, logger)
-
 	return &Service{
 		id:       cfg.GRPC.Namespace + "." + cfg.Service.Name,
 		log:      logger,
-		Config:   cfg,
-		provider: provider,
+		provider: search.NewProvider(gw, eng, extractor, logger, cfg.MachineAuthAPIKey),
 	}, nil
 }
 
@@ -80,8 +86,7 @@ func NewHandler(opts ...Option) (searchsvc.SearchProviderHandler, error) {
 type Service struct {
 	id       string
 	log      log.Logger
-	Config   *config.Config
-	provider search.ProviderClient
+	provider *search.Provider
 }
 
 func (s Service) Search(ctx context.Context, in *searchsvc.SearchRequest, out *searchsvc.SearchResponse) error {
@@ -112,6 +117,7 @@ func (s Service) Search(ctx context.Context, in *searchsvc.SearchRequest, out *s
 	return nil
 }
 
+// IndexSpace (re)indexes all resources of a given space.
 func (s Service) IndexSpace(ctx context.Context, in *searchsvc.IndexSpaceRequest, out *searchsvc.IndexSpaceResponse) error {
 	_, err := s.provider.IndexSpace(ctx, in)
 	return err
diff --git a/services/storage-users/pkg/config/config.go b/services/storage-users/pkg/config/config.go
index de39c232fcc..eb6a0aa8c17 100644
--- a/services/storage-users/pkg/config/config.go
+++ b/services/storage-users/pkg/config/config.go
@@ -94,7 +94,8 @@ type OCISDriver struct {
 	// the general space alias, eg: `{{.SpaceType}}/{{.SpaceName | replace " " "-" | lower}}`
 	GeneralSpaceAliasTemplate string `yaml:"generalspacealias_template" env:"STORAGE_USERS_OCIS_GENERAL_SPACE_ALIAS_TEMPLATE" desc:"Template string to construct general space aliases."`
 	//ShareFolder defines the name of the folder jailing all shares
-	ShareFolder string `yaml:"share_folder" env:"STORAGE_USERS_OCIS_SHARE_FOLDER" desc:"Name of the folder jailing all shares."`
+	ShareFolder  string `yaml:"share_folder" env:"STORAGE_USERS_OCIS_SHARE_FOLDER" desc:"Name of the folder jailing all shares."`
+	AsyncUploads bool   `yaml:"async_uploads" env:"STORAGE_USERS_OCIS_ASYNC_UPLOADS" desc:"Enable asynchronous file uploads."`
 }
 
 type S3NGDriver struct {
@@ -133,7 +134,7 @@ type OwnCloudSQLDriver struct {
 }
 
 type Events struct {
-	Addr      string `yaml:"endpoint" env:"STORAGE_USERS_EVENTS_ENDPOINT" desc:"The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture.`
+	Addr      string `yaml:"endpoint" env:"STORAGE_USERS_EVENTS_ENDPOINT" desc:"The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture."`
 	ClusterID string `yaml:"cluster" env:"STORAGE_USERS_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system."`
 }
 type S3Driver struct {
diff --git a/services/storage-users/pkg/config/defaults/defaultconfig.go b/services/storage-users/pkg/config/defaults/defaultconfig.go
index 116c3be0bd6..425cf9a17fd 100644
--- a/services/storage-users/pkg/config/defaults/defaultconfig.go
+++ b/services/storage-users/pkg/config/defaults/defaultconfig.go
@@ -73,6 +73,7 @@ func DefaultConfig() *config.Config {
 				PersonalSpaceAliasTemplate: "{{.SpaceType}}/{{.User.Username | lower}}",
 				GeneralSpaceAliasTemplate:  "{{.SpaceType}}/{{.SpaceName | replace \" \" \"-\" | lower}}",
 				PermissionsEndpoint:        "127.0.0.1:9191",
+				AsyncUploads:               false,
 			},
 		},
 		Events: config.Events{
diff --git a/services/storage-users/pkg/revaconfig/user.go b/services/storage-users/pkg/revaconfig/user.go
index a40331e20d0..b97344058bc 100644
--- a/services/storage-users/pkg/revaconfig/user.go
+++ b/services/storage-users/pkg/revaconfig/user.go
@@ -95,7 +95,7 @@ func UserDrivers(cfg *config.Config) map[string]interface{} {
 			"treetime_accounting":         true,
 			"treesize_accounting":         true,
 			"permissionssvc":              cfg.Drivers.OCIS.PermissionsEndpoint,
-			// "asyncfileuploads":            true,
+			"asyncfileuploads":            cfg.Drivers.OCIS.AsyncUploads,
 			"events": map[string]interface{}{
 				"natsaddress":   cfg.Events.Addr,
 				"natsclusterid": cfg.Events.ClusterID,


Test patch:
diff --git a/services/search/pkg/content/basic_test.go b/services/search/pkg/content/basic_test.go
new file mode 100644
index 00000000000..ebd2342eeb3
--- /dev/null
+++ b/services/search/pkg/content/basic_test.go
@@ -0,0 +1,65 @@
+package content_test
+
+import (
+	"context"
+	storageProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	cs3Types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"github.com/owncloud/ocis/v2/services/search/pkg/content"
+)
+
+var _ = Describe("Basic", func() {
+	var (
+		basic  content.Extractor
+		logger = log.NewLogger()
+		ref    = &storageProvider.Reference{}
+		ctx    = context.TODO()
+	)
+
+	BeforeEach(func() {
+		basic, _ = content.NewBasicExtractor(logger)
+		ref = &storageProvider.Reference{}
+	})
+
+	Describe("extract", func() {
+		It("basic fields", func() {
+			ri := &storageProvider.ResourceInfo{
+				Path:     "./foo/bar.pdf",
+				Size:     1024,
+				MimeType: "application/pdf",
+			}
+
+			doc, err := basic.Extract(ctx, ref, ri)
+
+			Expect(err).To(BeNil())
+			Expect(doc).ToNot(BeNil())
+			Expect(doc.Name).To(Equal(ri.Path))
+			Expect(doc.Size).To(Equal(ri.Size))
+			Expect(doc.MimeType).To(Equal(ri.MimeType))
+		})
+
+		It("RFC3339 mtime", func() {
+			for _, data := range []struct {
+				second uint64
+				expect string
+			}{
+				{second: 4000, expect: "1970-01-01T01:06:40Z"},
+				{second: 3000, expect: "1970-01-01T00:50:00Z"},
+				{expect: ""},
+			} {
+				ri := &storageProvider.ResourceInfo{}
+
+				if data.second != 0 {
+					ri.Mtime = &cs3Types.Timestamp{Seconds: data.second}
+				}
+
+				doc, err := basic.Extract(ctx, ref, ri)
+				Expect(err).To(BeNil())
+				Expect(doc).ToNot(BeNil())
+				Expect(doc.Mtime).To(Equal(data.expect))
+			}
+		})
+	})
+})
diff --git a/services/search/pkg/search/provider/provider_suite_test.go b/services/search/pkg/content/content_suite_test.go
similarity index 56%
rename from services/search/pkg/search/provider/provider_suite_test.go
rename to services/search/pkg/content/content_suite_test.go
index 9187db0278c..2d0d7468822 100644
--- a/services/search/pkg/search/provider/provider_suite_test.go
+++ b/services/search/pkg/content/content_suite_test.go
@@ -1,4 +1,4 @@
-package provider_test
+package content_test
 
 import (
 	"testing"
@@ -7,7 +7,7 @@ import (
 	. "github.com/onsi/gomega"
 )
 
-func TestProvider(t *testing.T) {
+func TestContent(t *testing.T) {
 	RegisterFailHandler(Fail)
-	RunSpecs(t, "Provider Suite")
+	RunSpecs(t, "Content Suite")
 }
diff --git a/services/search/pkg/content/tika_test.go b/services/search/pkg/content/tika_test.go
new file mode 100644
index 00000000000..61d8044d295
--- /dev/null
+++ b/services/search/pkg/content/tika_test.go
@@ -0,0 +1,87 @@
+package content_test
+
+import (
+	"context"
+	"fmt"
+	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	conf "github.com/owncloud/ocis/v2/services/search/pkg/config/defaults"
+	"github.com/owncloud/ocis/v2/services/search/pkg/content"
+	contentMocks "github.com/owncloud/ocis/v2/services/search/pkg/content/mocks"
+	"github.com/stretchr/testify/mock"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+)
+
+var _ = Describe("Tika", func() {
+	Describe("extract", func() {
+		var (
+			body     string
+			title    string
+			language string
+			version  string
+			srv      *httptest.Server
+			tika     *content.Tika
+		)
+
+		BeforeEach(func() {
+			body = ""
+			title = ""
+			language = ""
+			version = ""
+			srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+				out := ""
+				switch req.URL.Path {
+				case "/version":
+					out = version
+				case "/language/stream":
+					out = language
+				case "/rmeta/text":
+					out = fmt.Sprintf(`[{"X-TIKA:content":"%s"},{"title":"%s"}]`, body, title)
+				}
+
+				_, _ = w.Write([]byte(out))
+			}))
+
+			cfg := conf.DefaultConfig()
+			cfg.Extractor.Tika.TikaURL = srv.URL
+
+			var err error
+			tika, err = content.NewTikaExtractor(nil, log.NewLogger(), cfg)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(tika).ToNot(BeNil())
+
+			retriever := &contentMocks.Retriever{}
+			retriever.On("Retrieve", mock.Anything, mock.Anything, mock.Anything).Return(io.NopCloser(strings.NewReader(body)), nil)
+
+			tika.Retriever = retriever
+		})
+
+		AfterEach(func() {
+			srv.Close()
+		})
+
+		It("basic operations", func() {
+			body = "any body"
+			title = "any title"
+
+			doc, err := tika.Extract(context.TODO(), &provider.Reference{}, &provider.ResourceInfo{})
+			Expect(err).ToNot(HaveOccurred())
+			Expect(doc.Title).To(Equal(title))
+			Expect(doc.Content).To(Equal(body))
+		})
+
+		It("remove stop words", func() {
+			body = "body to test stop words!!! I, you, he, she, it, we, you, they, stay"
+			language = "en"
+
+			doc, err := tika.Extract(context.TODO(), &provider.Reference{}, &provider.ResourceInfo{})
+			Expect(err).ToNot(HaveOccurred())
+			Expect(doc.Content).To(Equal("body test stop words i stay"))
+		})
+	})
+})
diff --git a/services/search/pkg/engine/bleve_test.go b/services/search/pkg/engine/bleve_test.go
new file mode 100644
index 00000000000..15358925f61
--- /dev/null
+++ b/services/search/pkg/engine/bleve_test.go
@@ -0,0 +1,297 @@
+package engine_test
+
+import (
+	"context"
+	"fmt"
+	"github.com/blevesearch/bleve/v2"
+	sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
+	searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
+	"github.com/owncloud/ocis/v2/services/search/pkg/content"
+	"github.com/owncloud/ocis/v2/services/search/pkg/engine"
+)
+
+var _ = Describe("Bleve", func() {
+	var (
+		eng *engine.Bleve
+		idx bleve.Index
+		ctx context.Context
+
+		createEntity = func(id sprovider.ResourceId, doc content.Document) engine.Resource {
+			name := doc.Name
+
+			if name == "" {
+				name = "default.pdf"
+			}
+
+			return engine.Resource{
+				ID:       fmt.Sprintf("%s$%s!%s", id.StorageId, id.SpaceId, id.OpaqueId),
+				RootID:   fmt.Sprintf("%s$%s!%s", id.StorageId, id.SpaceId, id.OpaqueId),
+				Path:     fmt.Sprintf("./%s", name),
+				Document: doc,
+			}
+		}
+
+		doSearch = func(id sprovider.ResourceId, query string) (*searchsvc.SearchIndexResponse, error) {
+			return eng.Search(ctx, &searchsvc.SearchIndexRequest{
+				Query: query,
+				Ref: &searchmsg.Reference{
+					ResourceId: &searchmsg.ResourceID{
+						StorageId: id.StorageId,
+						SpaceId:   id.SpaceId,
+						OpaqueId:  id.OpaqueId,
+					},
+				},
+			})
+		}
+
+		assertDocCount = func(id sprovider.ResourceId, query string, expectedCount int) []*searchmsg.Match {
+			res, err := doSearch(id, query)
+
+			ExpectWithOffset(1, err).ToNot(HaveOccurred())
+			ExpectWithOffset(1, len(res.Matches)).To(Equal(expectedCount), "query returned unexpected number of results: "+query)
+			return res.Matches
+		}
+	)
+
+	BeforeEach(func() {
+		mapping, err := engine.BuildBleveMapping()
+		Expect(err).ToNot(HaveOccurred())
+
+		idx, err = bleve.NewMemOnly(mapping)
+		Expect(err).ToNot(HaveOccurred())
+
+		eng = engine.NewBleveEngine(idx)
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	Describe("New", func() {
+		It("returns a new index instance", func() {
+			b := engine.NewBleveEngine(idx)
+			Expect(b).ToNot(BeNil())
+		})
+	})
+
+	Describe("Search", func() {
+		Context("by other fields than filename", func() {
+			It("finds files by size", func() {
+				rid := sprovider.ResourceId{
+					StorageId: "1",
+					SpaceId:   "2",
+					OpaqueId:  "3",
+				}
+				r := createEntity(rid, content.Document{Size: 12345})
+				err := eng.Upsert(r.ID, r)
+				Expect(err).ToNot(HaveOccurred())
+
+				assertDocCount(rid, `Size:12345`, 1)
+				assertDocCount(rid, `Size:>1000`, 1)
+				assertDocCount(rid, `Size:<100000`, 1)
+
+				assertDocCount(rid, `Size:12344`, 0)
+				assertDocCount(rid, `Size:<1000`, 0)
+				assertDocCount(rid, `Size:>100000`, 0)
+			})
+		})
+
+		Context("by filename", func() {
+			It("finds files with spaces in the filename", func() {
+				rid := sprovider.ResourceId{
+					StorageId: "1",
+					SpaceId:   "2",
+					OpaqueId:  "3",
+				}
+				r := createEntity(rid, content.Document{Name: "Foo oo.pdf"})
+				err := eng.Upsert(r.ID, r)
+				Expect(err).ToNot(HaveOccurred())
+				assertDocCount(rid, `Name:foo\ o*`, 1)
+			})
+
+			It("finds files by digits in the filename", func() {
+				rid := sprovider.ResourceId{
+					StorageId: "1",
+					SpaceId:   "2",
+					OpaqueId:  "3",
+				}
+				r := createEntity(rid, content.Document{Name: "12345.pdf"})
+				err := eng.Upsert(r.ID, r)
+				Expect(err).ToNot(HaveOccurred())
+
+				assertDocCount(rid, `Name:1234*`, 1)
+			})
+
+			Context("with a file in the root of the space", func() {
+				It("scopes the search to the specified space", func() {
+					rid := sprovider.ResourceId{
+						StorageId: "1",
+						SpaceId:   "2",
+						OpaqueId:  "3",
+					}
+					r := createEntity(rid, content.Document{Name: "foo.pdf"})
+					err := eng.Upsert(r.ID, r)
+					Expect(err).ToNot(HaveOccurred())
+
+					assertDocCount(rid, `Name:foo.pdf`, 1)
+					assertDocCount(sprovider.ResourceId{
+						StorageId: "9",
+						SpaceId:   "8",
+						OpaqueId:  "7",
+					}, `Name:foo.pdf`, 0)
+				})
+			})
+
+			It("limits the search to the specified fields", func() {
+				rid := sprovider.ResourceId{
+					StorageId: "1",
+					SpaceId:   "2",
+					OpaqueId:  "3",
+				}
+				r := createEntity(rid, content.Document{Name: "bar.pdf", Size: 789})
+				err := eng.Upsert(r.ID, r)
+				Expect(err).ToNot(HaveOccurred())
+
+				assertDocCount(rid, `Name:bar.pdf`, 1)
+				assertDocCount(rid, `Size:789`, 1)
+				assertDocCount(rid, `Unknown:field`, 0)
+			})
+
+			It("returns the total number of hits", func() {
+				rid := sprovider.ResourceId{
+					StorageId: "1",
+					SpaceId:   "2",
+					OpaqueId:  "3",
+				}
+				r := createEntity(rid, content.Document{Name: "bar.pdf"})
+				err := eng.Upsert(r.ID, r)
+				Expect(err).ToNot(HaveOccurred())
+
+				res, err := doSearch(rid, "Name:bar*")
+				Expect(err).ToNot(HaveOccurred())
+				Expect(res.TotalMatches).To(Equal(int32(1)))
+			})
+
+			It("returns all desired fields", func() {
+				rid := sprovider.ResourceId{
+					StorageId: "1",
+					SpaceId:   "2",
+					OpaqueId:  "3",
+				}
+				r := createEntity(rid, content.Document{Name: "bar.pdf"})
+				r.Type = 3
+				r.MimeType = "application/pdf"
+
+				err := eng.Upsert(r.ID, r)
+				Expect(err).ToNot(HaveOccurred())
+
+				matches := assertDocCount(rid, fmt.Sprintf("Name:%s", r.Name), 1)
+				match := matches[0]
+				Expect(match.Entity.Ref.ResourceId.OpaqueId).To(Equal(rid.OpaqueId))
+				Expect(match.Entity.Ref.Path).To(Equal(r.Path))
+				Expect(match.Entity.Name).To(Equal(r.Name))
+				Expect(match.Entity.Size).To(Equal(r.Size))
+				Expect(match.Entity.Type).To(Equal(r.Type))
+				Expect(match.Entity.MimeType).To(Equal(r.MimeType))
+				Expect(match.Entity.Deleted).To(BeFalse())
+				Expect(match.Score > 0).To(BeTrue())
+			})
+
+			It("finds files by name, prefix or substring match", func() {
+				queries := []string{"foo.pdf", "foo*", "*oo.p*"}
+				for i, query := range queries {
+					rid := sprovider.ResourceId{
+						StorageId: string(rune(i + 1)),
+						SpaceId:   string(rune(i + 2)),
+						OpaqueId:  string(rune(i + 3)),
+					}
+
+					r := createEntity(rid, content.Document{Name: "foo.pdf"})
+					r.Size = uint64(i + 250)
+					err := eng.Upsert(r.ID, r)
+					Expect(err).ToNot(HaveOccurred())
+
+					matches := assertDocCount(rid, query, 1)
+					Expect(matches[0].Entity.Ref.ResourceId.OpaqueId).To(Equal(rid.OpaqueId))
+					Expect(matches[0].Entity.Ref.Path).To(Equal(r.Path))
+					Expect(matches[0].Entity.Id.OpaqueId).To(Equal(rid.OpaqueId))
+					Expect(matches[0].Entity.Name).To(Equal(r.Name))
+					Expect(matches[0].Entity.Size).To(Equal(r.Size))
+				}
+			})
+
+			It("uses a lower-case index", func() {
+				rid := sprovider.ResourceId{
+					StorageId: "1",
+					SpaceId:   "2",
+					OpaqueId:  "3",
+				}
+				r := createEntity(rid, content.Document{Name: "foo.pdf"})
+				r.Type = 3
+				r.MimeType = "application/pdf"
+
+				err := eng.Upsert(r.ID, r)
+				Expect(err).ToNot(HaveOccurred())
+
+				assertDocCount(rid, "Name:foo*", 1)
+				assertDocCount(rid, "Name:Foo*", 0)
+			})
+
+			Context("and an additional file in a subdirectory", func() {
+				var (
+					ridT sprovider.ResourceId
+					entT engine.Resource
+					ridD sprovider.ResourceId
+					entD engine.Resource
+				)
+
+				BeforeEach(func() {
+					ridT = sprovider.ResourceId{
+						StorageId: "1",
+						SpaceId:   "2",
+						OpaqueId:  "3",
+					}
+					entT = createEntity(ridT, content.Document{Name: "top.pdf"})
+					Expect(eng.Upsert(entT.ID, entT)).ToNot(HaveOccurred())
+
+					ridD = sprovider.ResourceId{
+						StorageId: "1",
+						SpaceId:   "2",
+						OpaqueId:  "4",
+					}
+					entD = createEntity(ridD, content.Document{Name: "deep.pdf"})
+					entD.Path = "./nested/deep.pdf"
+					Expect(eng.Upsert(entD.ID, entD)).ToNot(HaveOccurred())
+				})
+
+				It("finds files living deeper in the tree by filename, prefix or substring match", func() {
+					queries := []string{"deep.pdf", "dee*", "*ep.*"}
+					for _, query := range queries {
+						assertDocCount(ridD, query, 1)
+					}
+				})
+
+				It("does not find the higher levels when limiting the searched directory", func() {
+					res, err := eng.Search(ctx, &searchsvc.SearchIndexRequest{
+						Ref: &searchmsg.Reference{
+							ResourceId: &searchmsg.ResourceID{
+								StorageId: ridT.StorageId,
+								SpaceId:   ridT.SpaceId,
+								OpaqueId:  ridT.OpaqueId,
+							},
+							Path: "./nested/",
+						},
+						Query: "Name:top.pdf",
+					})
+					Expect(err).ToNot(HaveOccurred())
+					Expect(res).ToNot(BeNil())
+					Expect(len(res.Matches)).To(Equal(0))
+				})
+			})
+		})
+
+		Context("query", func() {
+
+		})
+	})
+})
diff --git a/services/search/pkg/search/index/index_suite_test.go b/services/search/pkg/engine/engine_suite_test.go
similarity index 58%
rename from services/search/pkg/search/index/index_suite_test.go
rename to services/search/pkg/engine/engine_suite_test.go
index 09099db1a4c..062fcadb182 100644
--- a/services/search/pkg/search/index/index_suite_test.go
+++ b/services/search/pkg/engine/engine_suite_test.go
@@ -1,4 +1,4 @@
-package index_test
+package engine_test
 
 import (
 	"testing"
@@ -7,7 +7,7 @@ import (
 	. "github.com/onsi/gomega"
 )
 
-func TestIndex(t *testing.T) {
+func TestEngine(t *testing.T) {
 	RegisterFailHandler(Fail)
-	RunSpecs(t, "Index Suite")
+	RunSpecs(t, "Engine Suite")
 }
diff --git a/services/search/pkg/search/events_test.go b/services/search/pkg/search/events_test.go
new file mode 100644
index 00000000000..cc7c0f98768
--- /dev/null
+++ b/services/search/pkg/search/events_test.go
@@ -0,0 +1,181 @@
+package search_test
+
+import (
+	"context"
+	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
+	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+	"github.com/cs3org/reva/v2/pkg/events"
+	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
+	cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"github.com/owncloud/ocis/v2/services/search/pkg/config"
+	"github.com/owncloud/ocis/v2/services/search/pkg/content"
+	contentMocks "github.com/owncloud/ocis/v2/services/search/pkg/content/mocks"
+	engineMocks "github.com/owncloud/ocis/v2/services/search/pkg/engine/mocks"
+	"github.com/owncloud/ocis/v2/services/search/pkg/search"
+	"github.com/stretchr/testify/mock"
+	mEvents "go-micro.dev/v4/events"
+)
+
+var _ = Describe("Events", func() {
+	var (
+		gw        *cs3mocks.GatewayAPIClient
+		engine    *engineMocks.Engine
+		extractor *contentMocks.Extractor
+		bus       events.Stream
+		ctx       context.Context
+
+		logger = log.NewLogger()
+		user   = &userv1beta1.User{
+			Id: &userv1beta1.UserId{
+				OpaqueId: "user",
+			},
+		}
+
+		ref = &provider.Reference{
+			ResourceId: &provider.ResourceId{
+				StorageId: "storageid",
+				OpaqueId:  "rootopaqueid",
+			},
+			Path: "./foo.pdf",
+		}
+		ri = &provider.ResourceInfo{
+			Id: &provider.ResourceId{
+				StorageId: "storageid",
+				OpaqueId:  "opaqueid",
+			},
+			Path: "foo.pdf",
+			Size: 12345,
+		}
+	)
+
+	BeforeEach(func() {
+		ctx = context.Background()
+		gw = &cs3mocks.GatewayAPIClient{}
+		engine = &engineMocks.Engine{}
+		bus, _ = mEvents.NewStream()
+		extractor = &contentMocks.Extractor{}
+
+		_ = search.HandleEvents(engine, extractor, gw, bus, logger, &config.Config{})
+
+		gw.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{
+			Status: status.NewOK(ctx),
+			Token:  "authtoken",
+		}, nil)
+		gw.On("Stat", mock.Anything, mock.Anything).Return(&provider.StatResponse{
+			Status: status.NewOK(context.Background()),
+			Info:   ri,
+		}, nil)
+		engine.On("DocCount").Return(uint64(1), nil)
+	})
+	Describe("events", func() {
+		It("triggers an index update when a file has been uploaded", func() {
+			called := false
+			extractor.Mock.On("Extract", mock.Anything, mock.Anything, mock.Anything).Return(content.Document{}, nil)
+			engine.On("Upsert", mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) {
+				called = true
+			})
+
+			_ = events.Publish(bus, events.FileUploaded{
+				Ref:       ref,
+				Executant: user.Id,
+			})
+
+			Eventually(func() bool {
+				return called
+			}, "2s").Should(BeTrue())
+		})
+
+		It("triggers an index update when a file has been touched", func() {
+			called := false
+			extractor.Mock.On("Extract", mock.Anything, mock.Anything, mock.Anything).Return(content.Document{}, nil)
+			engine.On("Upsert", mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) {
+				called = true
+			})
+			_ = events.Publish(bus, events.FileTouched{
+				Ref:       ref,
+				Executant: user.Id,
+			})
+
+			Eventually(func() bool {
+				return called
+			}, "2s").Should(BeTrue())
+		})
+
+		It("removes an entry from the index when the file has been deleted", func() {
+			called := false
+			gw.On("Stat", mock.Anything, mock.Anything).Return(&provider.StatResponse{
+				Status: status.NewNotFound(context.Background(), ""),
+			}, nil)
+			engine.On("Delete", mock.Anything).Return(nil).Run(func(args mock.Arguments) {
+				called = true
+			})
+
+			_ = events.Publish(bus, events.ItemTrashed{
+				Ref:       ref,
+				ID:        ri.Id,
+				Executant: user.Id,
+			})
+
+			Eventually(func() bool {
+				return called
+			}, "2s").Should(BeTrue())
+		})
+
+		It("indexes items when they are being restored", func() {
+			called := false
+			engine.On("Restore", mock.Anything).Return(nil).Run(func(args mock.Arguments) {
+				called = true
+			})
+
+			_ = events.Publish(bus, events.ItemRestored{
+				Ref:       ref,
+				Executant: user.Id,
+			})
+
+			Eventually(func() bool {
+				return called
+			}, "2s").Should(BeTrue())
+		})
+
+		It("indexes items when a version has been restored", func() {
+			called := false
+			extractor.Mock.On("Extract", mock.Anything, mock.Anything, mock.Anything).Return(content.Document{}, nil)
+			engine.On("Upsert", mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) {
+				called = true
+			})
+
+			_ = events.Publish(bus, events.FileVersionRestored{
+				Ref:       ref,
+				Executant: user.Id,
+			})
+
+			Eventually(func() bool {
+				return called
+			}, "2s").Should(BeTrue())
+		})
+
+		It("indexes items when they are being moved", func() {
+			called := false
+			gw.On("GetPath", mock.Anything, mock.Anything).Return(&provider.GetPathResponse{
+				Status: status.NewOK(ctx),
+				Path:   "",
+			}, nil)
+			engine.On("Move", mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) {
+				called = true
+			})
+
+			_ = events.Publish(bus, events.ItemMoved{
+				Ref:       ref,
+				Executant: user.Id,
+			})
+
+			Eventually(func() bool {
+				return called
+			}, "2s").Should(BeTrue())
+		})
+	})
+})
diff --git a/services/search/pkg/search/index/index_test.go b/services/search/pkg/search/index/index_test.go
deleted file mode 100644
index 7391d18a2e2..00000000000
--- a/services/search/pkg/search/index/index_test.go
+++ /dev/null
@@ -1,389 +0,0 @@
-package index_test
-
-import (
-	"context"
-
-	"github.com/blevesearch/bleve/v2"
-	sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
-	typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
-	searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
-	searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
-	"github.com/owncloud/ocis/v2/services/search/pkg/search/index"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-)
-
-var _ = Describe("Index", func() {
-	var (
-		i          *index.Index
-		bleveIndex bleve.Index
-		ctx        context.Context
-
-		rootId = &sprovider.ResourceId{
-			StorageId: "provider-1",
-			SpaceId:   "spaceid",
-			OpaqueId:  "rootopaqueid",
-		}
-		filename  string
-		ref       *sprovider.Reference
-		ri        *sprovider.ResourceInfo
-		parentRef = &sprovider.Reference{
-			ResourceId: rootId,
-			Path:       "./my/sub d!r",
-		}
-		parentRi = &sprovider.ResourceInfo{
-			Id: &sprovider.ResourceId{
-				StorageId: "provider-1",
-				SpaceId:   "spaceid",
-				OpaqueId:  "parentopaqueid",
-			},
-			Path:  "sub d!r",
-			Size:  12345,
-			Type:  sprovider.ResourceType_RESOURCE_TYPE_CONTAINER,
-			Mtime: &typesv1beta1.Timestamp{Seconds: 4000},
-		}
-		childRef = &sprovider.Reference{
-			ResourceId: rootId,
-			Path:       "./my/sub d!r/child.pdf",
-		}
-		childRi = &sprovider.ResourceInfo{
-			Id: &sprovider.ResourceId{
-				StorageId: "provider-1",
-				SpaceId:   "spaceid",
-				OpaqueId:  "childopaqueid",
-			},
-			ParentId: &sprovider.ResourceId{
-				StorageId: "provider-1",
-				SpaceId:   "spaceid",
-				OpaqueId:  "parentopaqueid",
-			},
-			Path:  "child.pdf",
-			Size:  12345,
-			Type:  sprovider.ResourceType_RESOURCE_TYPE_FILE,
-			Mtime: &typesv1beta1.Timestamp{Seconds: 4000},
-		}
-
-		assertDocCount = func(rootId *sprovider.ResourceId, query string, expectedCount int) []*searchmsg.Match {
-			res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
-				Query: query,
-				Ref: &searchmsg.Reference{
-					ResourceId: &searchmsg.ResourceID{
-						StorageId: "provider-1",
-						SpaceId:   rootId.SpaceId,
-						OpaqueId:  rootId.OpaqueId,
-					},
-				},
-			})
-			ExpectWithOffset(1, err).ToNot(HaveOccurred())
-			ExpectWithOffset(1, len(res.Matches)).To(Equal(expectedCount), "query returned unexpected number of results: "+query)
-			return res.Matches
-		}
-	)
-
-	BeforeEach(func() {
-		filename = "Foo.pdf"
-
-		mapping, err := index.BuildMapping()
-		Expect(err).ToNot(HaveOccurred())
-
-		bleveIndex, err = bleve.NewMemOnly(mapping)
-		Expect(err).ToNot(HaveOccurred())
-
-		i, err = index.New(bleveIndex)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	JustBeforeEach(func() {
-		ref = &sprovider.Reference{
-			ResourceId: rootId,
-			Path:       "./" + filename,
-		}
-		ri = &sprovider.ResourceInfo{
-			Id: &sprovider.ResourceId{
-				StorageId: "provider-1",
-				SpaceId:   "spaceid",
-				OpaqueId:  "opaqueid",
-			},
-			ParentId: &sprovider.ResourceId{
-				StorageId: "provider-1",
-				SpaceId:   "spaceid",
-				OpaqueId:  "someopaqueid",
-			},
-			Path:     filename,
-			Size:     12345,
-			Type:     sprovider.ResourceType_RESOURCE_TYPE_FILE,
-			MimeType: "application/pdf",
-			Mtime:    &typesv1beta1.Timestamp{Seconds: 4000},
-		}
-	})
-
-	Describe("New", func() {
-		It("returns a new index instance", func() {
-			i, err := index.New(bleveIndex)
-			Expect(err).ToNot(HaveOccurred())
-			Expect(i).ToNot(BeNil())
-		})
-	})
-
-	Describe("NewPersisted", func() {
-		It("returns a new index instance", func() {
-			i, err := index.NewPersisted("")
-			Expect(err).ToNot(HaveOccurred())
-			Expect(i).ToNot(BeNil())
-		})
-	})
-
-	Describe("Search", func() {
-		Context("by other fields than filename", func() {
-			JustBeforeEach(func() {
-				err := i.Add(ref, ri)
-				Expect(err).ToNot(HaveOccurred())
-			})
-
-			It("finds files by size", func() {
-				assertDocCount(ref.ResourceId, `Size:12345`, 1)
-				assertDocCount(ref.ResourceId, `Size:>1000`, 1)
-				assertDocCount(ref.ResourceId, `Size:<100000`, 1)
-
-				assertDocCount(ref.ResourceId, `Size:12344`, 0)
-				assertDocCount(ref.ResourceId, `Size:<1000`, 0)
-				assertDocCount(ref.ResourceId, `Size:>100000`, 0)
-			})
-		})
-
-		Context("by filename", func() {
-			It("finds files with spaces in the filename", func() {
-				ri.Path = "Foo oo.pdf"
-				ref.Path = "./" + ri.Path
-				err := i.Add(ref, ri)
-				Expect(err).ToNot(HaveOccurred())
-
-				assertDocCount(ref.ResourceId, `Name:foo\ o*`, 1)
-			})
-
-			It("finds files by digits in the filename", func() {
-				ri.Path = "12345.pdf"
-				ref.Path = "./" + ri.Path
-				err := i.Add(ref, ri)
-				Expect(err).ToNot(HaveOccurred())
-
-				assertDocCount(ref.ResourceId, `Name:1234*`, 1)
-			})
-
-			Context("with a file in the root of the space", func() {
-				JustBeforeEach(func() {
-					err := i.Add(ref, ri)
-					Expect(err).ToNot(HaveOccurred())
-				})
-
-				It("scopes the search to the specified space", func() {
-					resourceId := &sprovider.ResourceId{
-						StorageId: "provider-1",
-						SpaceId:   "differentspaceid",
-						OpaqueId:  "differentopaqueid",
-					}
-					assertDocCount(resourceId, `Name:foo.pdf`, 0)
-				})
-
-				It("limits the search to the specified fields", func() {
-					assertDocCount(ref.ResourceId, "Name:*"+ref.ResourceId.OpaqueId+"*", 0)
-				})
-
-				It("returns the total number of hits", func() {
-					res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
-						Query: "Name:foo.pdf",
-						Ref: &searchmsg.Reference{
-							ResourceId: &searchmsg.ResourceID{
-								StorageId: ref.ResourceId.StorageId,
-								SpaceId:   ref.ResourceId.SpaceId,
-								OpaqueId:  ref.ResourceId.OpaqueId,
-							},
-						},
-					})
-					Expect(err).ToNot(HaveOccurred())
-					Expect(res.TotalMatches).To(Equal(int32(1)))
-				})
-				It("returns all desired fields", func() {
-					matches := assertDocCount(ref.ResourceId, "Name:foo.pdf", 1)
-					match := matches[0]
-					Expect(match.Entity.Ref.ResourceId.OpaqueId).To(Equal(ref.ResourceId.OpaqueId))
-					Expect(match.Entity.Ref.Path).To(Equal(ref.Path))
-					Expect(match.Entity.Id.OpaqueId).To(Equal(ri.Id.OpaqueId))
-					Expect(match.Entity.Name).To(Equal(ri.Path))
-					Expect(match.Entity.Size).To(Equal(ri.Size))
-					Expect(match.Entity.Type).To(Equal(uint64(ri.Type)))
-					Expect(match.Entity.MimeType).To(Equal(ri.MimeType))
-					Expect(match.Entity.Deleted).To(BeFalse())
-					Expect(match.Score > 0).To(BeTrue())
-					Expect(uint64(match.Entity.LastModifiedTime.AsTime().Unix())).To(Equal(ri.Mtime.Seconds))
-				})
-
-				It("finds files by name, prefix or substring match", func() {
-					queries := []string{"foo.pdf", "foo*", "*oo.p*"}
-					for _, query := range queries {
-						matches := assertDocCount(ref.ResourceId, query, 1)
-						Expect(matches[0].Entity.Ref.ResourceId.OpaqueId).To(Equal(ref.ResourceId.OpaqueId))
-						Expect(matches[0].Entity.Ref.Path).To(Equal(ref.Path))
-						Expect(matches[0].Entity.Id.OpaqueId).To(Equal(ri.Id.OpaqueId))
-						Expect(matches[0].Entity.Name).To(Equal(ri.Path))
-						Expect(matches[0].Entity.Size).To(Equal(ri.Size))
-					}
-				})
-
-				It("uses a lower-case index", func() {
-					assertDocCount(ref.ResourceId, "Name:foo*", 1)
-					assertDocCount(ref.ResourceId, "Name:Foo*", 0)
-				})
-
-				Context("and an additional file in a subdirectory", func() {
-					var (
-						nestedRef *sprovider.Reference
-						nestedRI  *sprovider.ResourceInfo
-					)
-
-					BeforeEach(func() {
-						nestedRef = &sprovider.Reference{
-							ResourceId: &sprovider.ResourceId{
-								StorageId: "provider-1",
-								SpaceId:   "spaceid",
-								OpaqueId:  "rootopaqueid",
-							},
-							Path: "./nested/nestedpdf.pdf",
-						}
-						nestedRI = &sprovider.ResourceInfo{
-							Id: &sprovider.ResourceId{
-								StorageId: "provider-1",
-								SpaceId:   "spaceid",
-								OpaqueId:  "nestedopaqueid",
-							},
-							Path: "nestedpdf.pdf",
-							Size: 12345,
-						}
-						err := i.Add(nestedRef, nestedRI)
-						Expect(err).ToNot(HaveOccurred())
-					})
-
-					It("finds files living deeper in the tree by filename, prefix or substring match", func() {
-						queries := []string{"nestedpdf.pdf", "nested*", "*tedpdf.*"}
-						for _, query := range queries {
-							assertDocCount(ref.ResourceId, query, 1)
-						}
-					})
-
-					It("does not find the higher levels when limiting the searched directory", func() {
-						res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
-							Ref: &searchmsg.Reference{
-								ResourceId: &searchmsg.ResourceID{
-									StorageId: ref.ResourceId.StorageId,
-									SpaceId:   ref.ResourceId.SpaceId,
-									OpaqueId:  ref.ResourceId.OpaqueId,
-								},
-								Path: "./nested/",
-							},
-							Query: "Name:foo.pdf",
-						})
-						Expect(err).ToNot(HaveOccurred())
-						Expect(res).ToNot(BeNil())
-						Expect(len(res.Matches)).To(Equal(0))
-					})
-				})
-			})
-		})
-	})
-
-	Describe("Add", func() {
-		It("adds a resourceInfo to the index", func() {
-			err := i.Add(ref, ri)
-			Expect(err).ToNot(HaveOccurred())
-
-			count, err := bleveIndex.DocCount()
-			Expect(err).ToNot(HaveOccurred())
-			Expect(count).To(Equal(uint64(1)))
-
-			query := bleve.NewMatchQuery("foo.pdf")
-			res, err := bleveIndex.Search(bleve.NewSearchRequest(query))
-			Expect(err).ToNot(HaveOccurred())
-			Expect(res.Hits.Len()).To(Equal(1))
-		})
-
-		It("updates an existing resource in the index", func() {
-			err := i.Add(ref, ri)
-			Expect(err).ToNot(HaveOccurred())
-			count, _ := bleveIndex.DocCount()
-			Expect(count).To(Equal(uint64(1)))
-
-			err = i.Add(ref, ri)
-			Expect(err).ToNot(HaveOccurred())
-			count, _ = bleveIndex.DocCount()
-			Expect(count).To(Equal(uint64(1)))
-		})
-	})
-
-	Describe("Delete", func() {
-		It("marks a resource as deleted", func() {
-			err := i.Add(parentRef, parentRi)
-			Expect(err).ToNot(HaveOccurred())
-			assertDocCount(rootId, `sub\ d!r`, 1)
-
-			err = i.Delete(parentRi.Id)
-			Expect(err).ToNot(HaveOccurred())
-
-			assertDocCount(rootId, `sub\ d!r`, 0)
-		})
-
-		It("also marks child resources as deleted", func() {
-			err := i.Add(parentRef, parentRi)
-			Expect(err).ToNot(HaveOccurred())
-			err = i.Add(childRef, childRi)
-			Expect(err).ToNot(HaveOccurred())
-
-			assertDocCount(rootId, `sub\ d\!r`, 1)
-			assertDocCount(rootId, "child.pdf", 1)
-
-			err = i.Delete(parentRi.Id)
-			Expect(err).ToNot(HaveOccurred())
-
-			assertDocCount(rootId, `sub\ d\!r`, 0)
-			assertDocCount(rootId, "child.pdf", 0)
-		})
-	})
-
-	Describe("Restore", func() {
-		It("also marks child resources as restored", func() {
-			err := i.Add(parentRef, parentRi)
-			Expect(err).ToNot(HaveOccurred())
-			err = i.Add(childRef, childRi)
-			Expect(err).ToNot(HaveOccurred())
-			err = i.Delete(parentRi.Id)
-			Expect(err).ToNot(HaveOccurred())
-
-			assertDocCount(rootId, `sub\ d!r`, 0)
-			assertDocCount(rootId, "child.pdf", 0)
-
-			err = i.Restore(parentRi.Id)
-			Expect(err).ToNot(HaveOccurred())
-
-			assertDocCount(rootId, `sub\ d!r`, 1)
-			assertDocCount(rootId, "child.pdf", 1)
-		})
-	})
-
-	Describe("Move", func() {
-		It("moves the parent and its child resources", func() {
-			err := i.Add(parentRef, parentRi)
-			Expect(err).ToNot(HaveOccurred())
-			err = i.Add(childRef, childRi)
-			Expect(err).ToNot(HaveOccurred())
-
-			parentRi.Path = "newname"
-			err = i.Move(parentRi.Id, "./somewhere/else/newname")
-			Expect(err).ToNot(HaveOccurred())
-
-			assertDocCount(rootId, `sub\ d!r`, 0)
-
-			matches := assertDocCount(rootId, "Name:child.pdf", 1)
-			Expect(matches[0].Entity.Ref.Path).To(Equal("./somewhere/else/newname/child.pdf"))
-		})
-	})
-})
diff --git a/services/search/pkg/search/provider/events_test.go b/services/search/pkg/search/provider/events_test.go
deleted file mode 100644
index b1c9dfdc513..00000000000
--- a/services/search/pkg/search/provider/events_test.go
+++ /dev/null
@@ -1,192 +0,0 @@
-package provider_test
-
-import (
-	"context"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	"github.com/stretchr/testify/mock"
-
-	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
-	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
-	sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
-	"github.com/cs3org/reva/v2/pkg/events"
-	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
-	cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
-	"github.com/owncloud/ocis/v2/ocis-pkg/log"
-	"github.com/owncloud/ocis/v2/services/search/pkg/search/mocks"
-	provider "github.com/owncloud/ocis/v2/services/search/pkg/search/provider"
-)
-
-var _ = Describe("Searchprovider", func() {
-	var (
-		p           *provider.Provider
-		gwClient    *cs3mocks.GatewayAPIClient
-		indexClient *mocks.IndexClient
-
-		ctx        context.Context
-		eventsChan chan interface{}
-
-		logger = log.NewLogger()
-		user   = &userv1beta1.User{
-			Id: &userv1beta1.UserId{
-				OpaqueId: "user",
-			},
-		}
-
-		ref = &sprovider.Reference{
-			ResourceId: &sprovider.ResourceId{
-				StorageId: "storageid",
-				OpaqueId:  "rootopaqueid",
-			},
-			Path: "./foo.pdf",
-		}
-		ri = &sprovider.ResourceInfo{
-			Id: &sprovider.ResourceId{
-				StorageId: "storageid",
-				OpaqueId:  "opaqueid",
-			},
-			Path: "foo.pdf",
-			Size: 12345,
-		}
-	)
-
-	BeforeEach(func() {
-		ctx = context.Background()
-		eventsChan = make(chan interface{})
-		gwClient = &cs3mocks.GatewayAPIClient{}
-		indexClient = &mocks.IndexClient{}
-
-		p = provider.New(gwClient, indexClient, "", eventsChan, logger)
-
-		gwClient.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{
-			Status: status.NewOK(ctx),
-			Token:  "authtoken",
-		}, nil)
-		gwClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{
-			Status: status.NewOK(context.Background()),
-			Info:   ri,
-		}, nil)
-		indexClient.On("DocCount").Return(uint64(1), nil)
-	})
-
-	Describe("New", func() {
-		It("returns a new instance", func() {
-			p = provider.New(gwClient, indexClient, "", eventsChan, logger)
-			Expect(p).ToNot(BeNil())
-		})
-	})
-
-	Describe("events", func() {
-		It("triggers an index update when a file has been uploaded", func() {
-			called := false
-			indexClient.On("Add", mock.Anything, mock.MatchedBy(func(riToIndex *sprovider.ResourceInfo) bool {
-				return riToIndex.Id.OpaqueId == ri.Id.OpaqueId
-			})).Return(nil).Run(func(args mock.Arguments) {
-				called = true
-			})
-			eventsChan <- events.FileUploaded{
-				Ref:       ref,
-				Executant: user.Id,
-			}
-
-			Eventually(func() bool {
-				return called
-			}, "2s").Should(BeTrue())
-		})
-
-		It("triggers an index update when a file has been touched", func() {
-			called := false
-			indexClient.On("Add", mock.Anything, mock.MatchedBy(func(riToIndex *sprovider.ResourceInfo) bool {
-				return riToIndex.Id.OpaqueId == ri.Id.OpaqueId
-			})).Return(nil).Run(func(args mock.Arguments) {
-				called = true
-			})
-			eventsChan <- events.FileTouched{
-				Ref:       ref,
-				Executant: user.Id,
-			}
-
-			Eventually(func() bool {
-				return called
-			}, "2s").Should(BeTrue())
-		})
-
-		It("removes an entry from the index when the file has been deleted", func() {
-			called := false
-			gwClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{
-				Status: status.NewNotFound(context.Background(), ""),
-			}, nil)
-			indexClient.On("Delete", mock.MatchedBy(func(id *sprovider.ResourceId) bool {
-				return id.OpaqueId == ri.Id.OpaqueId
-			})).Return(nil).Run(func(args mock.Arguments) {
-				called = true
-			})
-			eventsChan <- events.ItemTrashed{
-				Ref:       ref,
-				ID:        ri.Id,
-				Executant: user.Id,
-			}
-
-			Eventually(func() bool {
-				return called
-			}, "2s").Should(BeTrue())
-		})
-
-		It("indexes items when they are being restored", func() {
-			called := false
-			indexClient.On("Restore", mock.MatchedBy(func(id *sprovider.ResourceId) bool {
-				return id.OpaqueId == ri.Id.OpaqueId
-			})).Return(nil).Run(func(args mock.Arguments) {
-				called = true
-			})
-			eventsChan <- events.ItemRestored{
-				Ref:       ref,
-				Executant: user.Id,
-			}
-
-			Eventually(func() bool {
-				return called
-			}, "2s").Should(BeTrue())
-		})
-
-		It("indexes items when a version has been restored", func() {
-			called := false
-			indexClient.On("Add", mock.Anything, mock.MatchedBy(func(riToIndex *sprovider.ResourceInfo) bool {
-				return riToIndex.Id.OpaqueId == ri.Id.OpaqueId
-			})).Return(nil).Run(func(args mock.Arguments) {
-				called = true
-			})
-			eventsChan <- events.FileVersionRestored{
-				Ref:       ref,
-				Executant: user.Id,
-			}
-
-			Eventually(func() bool {
-				return called
-			}, "2s").Should(BeTrue())
-		})
-
-		It("indexes items when they are being moved", func() {
-			called := false
-			gwClient.On("GetPath", mock.Anything, mock.Anything).Return(&sprovider.GetPathResponse{
-				Status: status.NewOK(ctx),
-				Path:   "./new/path.pdf",
-			}, nil)
-			indexClient.On("Move", mock.MatchedBy(func(id *sprovider.ResourceId) bool {
-				return id.OpaqueId == ri.Id.OpaqueId
-			}), "./new/path.pdf").Return(nil).Run(func(args mock.Arguments) {
-				called = true
-			})
-			ref.Path = "./new/path.pdf"
-			eventsChan <- events.ItemMoved{
-				Ref:       ref,
-				Executant: user.Id,
-			}
-
-			Eventually(func() bool {
-				return called
-			}, "2s").Should(BeTrue())
-		})
-	})
-})
diff --git a/services/search/pkg/search/provider/searchprovider_test.go b/services/search/pkg/search/provider_test.go
similarity index 76%
rename from services/search/pkg/search/provider/searchprovider_test.go
rename to services/search/pkg/search/provider_test.go
index f16e6f6f1f8..fde7b12b3b3 100644
--- a/services/search/pkg/search/provider/searchprovider_test.go
+++ b/services/search/pkg/search/provider_test.go
@@ -1,36 +1,34 @@
-package provider_test
+package search_test
 
 import (
 	"context"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	"github.com/stretchr/testify/mock"
-
 	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
 	userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
 	sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
 	typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
 	"github.com/cs3org/reva/v2/pkg/rgrpc/status"
 	cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
 	searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
 	searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
-	"github.com/owncloud/ocis/v2/services/search/pkg/search/mocks"
-	provider "github.com/owncloud/ocis/v2/services/search/pkg/search/provider"
+	"github.com/owncloud/ocis/v2/services/search/pkg/content"
+	contentMocks "github.com/owncloud/ocis/v2/services/search/pkg/content/mocks"
+	engineMocks "github.com/owncloud/ocis/v2/services/search/pkg/engine/mocks"
+	"github.com/owncloud/ocis/v2/services/search/pkg/search"
+	"github.com/stretchr/testify/mock"
 )
 
 var _ = Describe("Searchprovider", func() {
 	var (
-		p           *provider.Provider
-		gwClient    *cs3mocks.GatewayAPIClient
-		indexClient *mocks.IndexClient
-
-		ctx        context.Context
-		eventsChan chan interface{}
-
-		logger = log.NewLogger()
-		user   = &userv1beta1.User{
+		p           *search.Provider
+		extractor   *contentMocks.Extractor
+		gw          *cs3mocks.GatewayAPIClient
+		indexClient *engineMocks.Engine
+		ctx         context.Context
+		logger      = log.NewLogger()
+		user        = &userv1beta1.User{
 			Id: &userv1beta1.UserId{
 				OpaqueId: "user",
 			},
@@ -66,17 +64,17 @@ var _ = Describe("Searchprovider", func() {
 
 	BeforeEach(func() {
 		ctx = context.Background()
-		eventsChan = make(chan interface{})
-		gwClient = &cs3mocks.GatewayAPIClient{}
-		indexClient = &mocks.IndexClient{}
+		gw = &cs3mocks.GatewayAPIClient{}
+		indexClient = &engineMocks.Engine{}
+		extractor = &contentMocks.Extractor{}
 
-		p = provider.New(gwClient, indexClient, "", eventsChan, logger)
+		p = search.NewProvider(gw, indexClient, extractor, logger, "")
 
-		gwClient.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{
+		gw.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{
 			Status: status.NewOK(ctx),
 			Token:  "authtoken",
 		}, nil)
-		gwClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{
+		gw.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{
 			Status: status.NewOK(context.Background()),
 			Info:   ri,
 		}, nil)
@@ -85,20 +83,19 @@ var _ = Describe("Searchprovider", func() {
 
 	Describe("New", func() {
 		It("returns a new instance", func() {
-			p := provider.New(gwClient, indexClient, "", eventsChan, logger)
+			p := search.NewProvider(gw, indexClient, extractor, logger, "")
 			Expect(p).ToNot(BeNil())
 		})
 	})
 
 	Describe("IndexSpace", func() {
 		It("walks the space and indexes all files", func() {
-			gwClient.On("GetUserByClaim", mock.Anything, mock.Anything).Return(&userv1beta1.GetUserByClaimResponse{
+			gw.On("GetUserByClaim", mock.Anything, mock.Anything).Return(&userv1beta1.GetUserByClaimResponse{
 				Status: status.NewOK(context.Background()),
 				User:   user,
 			}, nil)
-			indexClient.On("Add", mock.Anything, mock.MatchedBy(func(riToIndex *sprovider.ResourceInfo) bool {
-				return riToIndex.Id.OpaqueId == ri.Id.OpaqueId
-			})).Return(nil)
+			extractor.Mock.On("Extract", mock.Anything, mock.Anything, mock.Anything).Return(content.Document{}, nil)
+			indexClient.On("Upsert", mock.Anything, mock.Anything).Return(nil)
 
 			res, err := p.IndexSpace(ctx, &searchsvc.IndexSpaceRequest{
 				SpaceId: "storageid",
@@ -120,7 +117,7 @@ var _ = Describe("Searchprovider", func() {
 
 		Context("with a personal space", func() {
 			BeforeEach(func() {
-				gwClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{
+				gw.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{
 					Status:        status.NewOK(ctx),
 					StorageSpaces: []*sprovider.StorageSpace{personalSpace},
 				}, nil)
@@ -149,50 +146,16 @@ var _ = Describe("Searchprovider", func() {
 				}, nil)
 			})
 
-			It("lowercases the filename", func() {
-				p.Search(ctx, &searchsvc.SearchRequest{
-					Query: "Foo.pdf",
-				})
-				indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
-					return req.Query == "Name:*foo.pdf*"
-				}))
-			})
-
 			It("does not mess with field-based searches", func() {
-				p.Search(ctx, &searchsvc.SearchRequest{
+				_, err := p.Search(ctx, &searchsvc.SearchRequest{
 					Query: "Size:<10",
 				})
+				Expect(err).ToNot(HaveOccurred())
 				indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
 					return req.Query == "Size:<10"
 				}))
 			})
 
-			It("uppercases field names", func() {
-				tests := []struct {
-					Original string
-					Expected string
-				}{
-					{Original: "size:<100", Expected: "Size:<100"},
-				}
-				for _, test := range tests {
-					p.Search(ctx, &searchsvc.SearchRequest{
-						Query: test.Original,
-					})
-					indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
-						return req.Query == test.Expected
-					}))
-				}
-			})
-
-			It("escapes special characters", func() {
-				p.Search(ctx, &searchsvc.SearchRequest{
-					Query: "Foo oo.pdf",
-				})
-				indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
-					return req.Query == `Name:*foo\ oo.pdf*`
-				}))
-			})
-
 			It("searches the personal user space", func() {
 				res, err := p.Search(ctx, &searchsvc.SearchRequest{
 					Query: "foo",
@@ -206,10 +169,6 @@ var _ = Describe("Searchprovider", func() {
 				Expect(match.Entity.Name).To(Equal("Foo.pdf"))
 				Expect(match.Entity.Ref.ResourceId.OpaqueId).To(Equal(personalSpace.Root.OpaqueId))
 				Expect(match.Entity.Ref.Path).To(Equal("./path/to/Foo.pdf"))
-
-				indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
-					return req.Query == "Name:*foo*" && req.Ref.ResourceId.OpaqueId == personalSpace.Root.OpaqueId && req.Ref.Path == ""
-				}))
 			})
 		})
 
@@ -241,14 +200,14 @@ var _ = Describe("Searchprovider", func() {
 						},
 					},
 				}
-				gwClient.On("GetPath", mock.Anything, mock.Anything).Return(&sprovider.GetPathResponse{
+				gw.On("GetPath", mock.Anything, mock.Anything).Return(&sprovider.GetPathResponse{
 					Status: status.NewOK(ctx),
 					Path:   "/grant/path",
 				}, nil)
 			})
 
 			It("searches the received spaces", func() {
-				gwClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{
+				gw.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{
 					Status:        status.NewOK(ctx),
 					StorageSpaces: []*sprovider.StorageSpace{grantSpace, mountpointSpace},
 				}, nil)
@@ -286,15 +245,11 @@ var _ = Describe("Searchprovider", func() {
 				Expect(match.Entity.Name).To(Equal("Shared.pdf"))
 				Expect(match.Entity.Ref.ResourceId.OpaqueId).To(Equal(mountpointSpace.Root.OpaqueId))
 				Expect(match.Entity.Ref.Path).To(Equal("./to/Shared.pdf"))
-
-				indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
-					return req.Query == "Name:*foo*" && req.Ref.ResourceId.StorageId == grantSpace.Root.StorageId && req.Ref.Path == "./grant/path"
-				}))
 			})
 
 			Context("when searching both spaces", func() {
 				BeforeEach(func() {
-					gwClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{
+					gw.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{
 						Status:        status.NewOK(ctx),
 						StorageSpaces: []*sprovider.StorageSpace{personalSpace, grantSpace, mountpointSpace},
 					}, nil)