-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support index pattern #316
Changes from 5 commits
ec9299c
b6485b0
4b2ccc4
eae71b5
ccb226f
0a0d319
3b8af18
5d969e3
2a8ca4b
f304e76
b154c99
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,4 +106,9 @@ val request1WithSort: SearchRequest = search(index = IndexName("index"), query = | |
val request2WithSort: SearchAndAggregateRequest = search(index = IndexName("index"), query = matchAll, aggregation = maxAggregation(name = "aggregation", field = "intField")).sort(sortBy("intField").missing(First)) | ||
``` | ||
|
||
If you want to create `Search` request with `IndexPattern`, you can do that in the following manner: | ||
```scala | ||
val requestWithIndexPattern: SearchRequest = search(index = IndexPattern("index*"), query = matchAll) | ||
``` | ||
|
||
You can find more information about `Search` and `SearchAndAggregate` requests [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-search.html). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add new line at the end of the file? |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -686,16 +686,23 @@ object HttpExecutorSpec extends IntegrationSpec { | |||||
), | ||||||
suite("refresh index")( | ||||||
test("successfully refresh existing index") { | ||||||
assertZIO(Executor.execute(ElasticRequest.refresh(index)))( | ||||||
equalTo(true) | ||||||
) | ||||||
assertZIO(Executor.execute(ElasticRequest.refresh(index)))(equalTo(true)) | ||||||
}, | ||||||
test("successfully refresh more existing indices") { | ||||||
for { | ||||||
_ <- Executor.execute(ElasticRequest.createIndex(createIndexTestName)) | ||||||
res <- Executor.execute(ElasticRequest.refresh(MultiIndex.names(index, createIndexTestName))) | ||||||
} yield assert(res)(equalTo(true)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's replace all |
||||||
}, | ||||||
test("successfully refresh all indices") { | ||||||
assertZIO(Executor.execute(ElasticRequest.refresh(IndexPatternAll)))(equalTo(true)) | ||||||
}, | ||||||
test("return false if index does not exists") { | ||||||
assertZIO(Executor.execute(ElasticRequest.refresh(refreshFailIndex)))( | ||||||
equalTo(false) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess you can use |
||||||
) | ||||||
} | ||||||
), | ||||||
) @@ after(Executor.execute(ElasticRequest.deleteIndex(createIndexTestName)).orDie), | ||||||
suite("retrieving document by IDs")( | ||||||
test("find documents by ids") { | ||||||
checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument) { | ||||||
|
@@ -979,6 +986,36 @@ object HttpExecutorSpec extends IntegrationSpec { | |||||
Executor.execute(ElasticRequest.createIndex(firstSearchIndex)), | ||||||
Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie | ||||||
), | ||||||
test("search for a document using a match all query with index pattern") { | ||||||
checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument) { | ||||||
(firstDocumentId, firstDocument, secondDocumentId, secondDocument) => | ||||||
for { | ||||||
_ <- Executor.execute(ElasticRequest.deleteByQuery(firstSearchIndex, matchAll)) | ||||||
_ <- Executor.execute(ElasticRequest.deleteByQuery(secondSearchIndex, matchAll)) | ||||||
document = firstDocument.copy(stringField = s"this is test") | ||||||
_ <- | ||||||
Executor.execute( | ||||||
ElasticRequest.upsert[TestDocument](firstSearchIndex, firstDocumentId, document).refreshTrue | ||||||
) | ||||||
secondDocumentCopy = secondDocument.copy(stringField = s"this is test") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
_ <- Executor.execute( | ||||||
ElasticRequest | ||||||
.upsert[TestDocument](secondSearchIndex, secondDocumentId, secondDocumentCopy) | ||||||
.refreshTrue | ||||||
) | ||||||
query = matchAll | ||||||
res <- Executor | ||||||
.execute(ElasticRequest.search(IndexPattern("search-index*"), query)) | ||||||
.documentAs[TestDocument] | ||||||
} yield assert(res)(Assertion.contains(document)) && assert(res)(Assertion.contains(secondDocumentCopy)) | ||||||
} | ||||||
} @@ around( | ||||||
Executor.execute(ElasticRequest.createIndex(firstSearchIndex)), | ||||||
Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie | ||||||
) @@ around( | ||||||
Executor.execute(ElasticRequest.createIndex(secondSearchIndex)), | ||||||
Executor.execute(ElasticRequest.deleteIndex(secondSearchIndex)).orDie | ||||||
), | ||||||
test("search for a document using a match boolean prefix query") { | ||||||
checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument) { | ||||||
(firstDocumentId, firstDocument, secondDocumentId, secondDocument) => | ||||||
|
@@ -1000,6 +1037,40 @@ object HttpExecutorSpec extends IntegrationSpec { | |||||
Executor.execute(ElasticRequest.createIndex(firstSearchIndex)), | ||||||
Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie | ||||||
), | ||||||
test("search for a document using a match phrase query with multi index") { | ||||||
checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument) { | ||||||
(firstDocumentId, firstDocument, secondDocumentId, secondDocument) => | ||||||
for { | ||||||
_ <- Executor.execute(ElasticRequest.deleteByQuery(firstSearchIndex, matchAll)) | ||||||
_ <- Executor.execute(ElasticRequest.deleteByQuery(secondSearchIndex, matchAll)) | ||||||
document = firstDocument.copy(stringField = s"this is test") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since you named copy of |
||||||
_ <- | ||||||
Executor.execute( | ||||||
ElasticRequest.upsert[TestDocument](firstSearchIndex, firstDocumentId, document).refreshTrue | ||||||
) | ||||||
secondDocumentCopy = secondDocument.copy(stringField = s"this is test") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
_ <- Executor.execute( | ||||||
ElasticRequest | ||||||
.upsert[TestDocument](secondSearchIndex, secondDocumentId, secondDocumentCopy) | ||||||
.refreshTrue | ||||||
) | ||||||
query = matchPhrase( | ||||||
field = TestDocument.stringField, | ||||||
value = document.stringField | ||||||
) | ||||||
|
||||||
res <- Executor | ||||||
.execute(ElasticRequest.search(MultiIndex.names(firstSearchIndex, secondSearchIndex), query)) | ||||||
.documentAs[TestDocument] | ||||||
} yield assert(res)(Assertion.contains(document)) && assert(res)(Assertion.contains(secondDocumentCopy)) | ||||||
} | ||||||
} @@ around( | ||||||
Executor.execute(ElasticRequest.createIndex(firstSearchIndex)), | ||||||
Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie | ||||||
) @@ around( | ||||||
Executor.execute(ElasticRequest.createIndex(secondSearchIndex)), | ||||||
Executor.execute(ElasticRequest.deleteIndex(secondSearchIndex)).orDie | ||||||
), | ||||||
test("search for a document using a match phrase query") { | ||||||
checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument) { | ||||||
(firstDocumentId, firstDocument, secondDocumentId, secondDocument) => | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,8 @@ trait IntegrationSpec extends ZIOSpecDefault { | |
|
||
val refreshFailIndex: IndexName = IndexName("refresh-fail") | ||
|
||
val IndexPatternAll: IndexPattern = IndexPattern("_all") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use a constant? |
||
|
||
val prepareElasticsearchIndexForTests: TestAspect[Nothing, Any, Throwable, Any] = beforeAll((for { | ||
_ <- Executor.execute(ElasticRequest.createIndex(index)) | ||
_ <- Executor.execute(ElasticRequest.deleteByQuery(index, matchAll).refreshTrue) | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright 2022 LambdaWorks | ||
* | ||
* 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. | ||
*/ | ||
|
||
package zio.elasticsearch | ||
|
||
import org.apache.commons.lang3.StringUtils | ||
import org.apache.commons.lang3.StringUtils.{equalsAny, startsWithAny} | ||
import zio.Chunk | ||
|
||
object IndexPatternValidation { | ||
def isValid(pattern: String): Boolean = { | ||
def containsAny(string: String, params: Chunk[String]): Boolean = | ||
params.exists(StringUtils.contains(string, _)) | ||
|
||
pattern.toLowerCase == pattern && | ||
pattern.nonEmpty && | ||
!startsWithAny(pattern, "+") && | ||
!containsAny(string = pattern, params = Chunk("?", "\"", "<", ">", "|", " ", ",", "#", ":")) && | ||
!equalsAny(pattern, ".", "..") && | ||
pattern.getBytes().length <= 255 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
* Copyright 2022 LambdaWorks | ||
* | ||
* 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. | ||
*/ | ||
|
||
package zio.elasticsearch | ||
|
||
import zio.Chunk | ||
import zio.prelude.AssertionError.failure | ||
import zio.prelude.Newtype | ||
|
||
trait IndexSelector[A] { | ||
def toSelector(a: A): String | ||
} | ||
|
||
object IndexSelector { | ||
|
||
implicit object IndexNameSelector extends IndexSelector[IndexName] { | ||
def toSelector(name: IndexName): String = IndexName.unwrap(name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put implementation in the new line. |
||
} | ||
|
||
implicit object IndexPatternSelector extends IndexSelector[IndexPattern] { | ||
def toSelector(pattern: IndexPattern): String = IndexPattern.unwrap(pattern) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. |
||
} | ||
|
||
implicit object MultiIndexSelector extends IndexSelector[MultiIndex] { | ||
def toSelector(multi: MultiIndex): String = multi.indices.mkString(",") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. |
||
} | ||
|
||
implicit class IndexNameSyntax[A](a: A)(implicit IS: IndexSelector[A]) { | ||
def toSelector: String = IS.toSelector(a) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. |
||
} | ||
|
||
} | ||
|
||
trait IndexNameNewtype { | ||
object IndexName extends Newtype[String] { | ||
override def assertion = assertCustom { (name: String) => // scalafix:ok | ||
if (IndexNameValidation.isValid(name)) { | ||
Right(()) | ||
} else { | ||
Left( | ||
failure( | ||
s""" | ||
| - Must be lower case only | ||
| - Cannot include \\, /, *, ?, ", <, >, |, ` `(space character), `,`(comma), #. | ||
| - Cannot include ":"(since 7.0). | ||
| - Cannot be empty | ||
| - Cannot start with -, _, +. | ||
| - Cannot be `.` or `..`. | ||
| - Cannot be longer than 255 bytes (note it is bytes, so multi-byte characters will count towards the 255 limit faster). | ||
| - Names starting with . are deprecated, except for hidden indices and internal indices managed by plugins. | ||
|""".stripMargin | ||
) | ||
) | ||
} | ||
} | ||
} | ||
type IndexName = IndexName.Type | ||
} | ||
|
||
trait IndexPatternNewType { | ||
object IndexPattern extends Newtype[String] { | ||
override def assertion = assertCustom { (pattern: String) => // scalafix:ok | ||
if (IndexPatternValidation.isValid(pattern)) { | ||
Right(()) | ||
} else { | ||
Left( | ||
failure( | ||
s""" | ||
| - Must be lower case only | ||
| - Cannot include \\, /, ?, ", <, >, |, ` `(space character), `,`(comma), #. | ||
| - Cannot include ":"(since 7.0). | ||
| - Cannot be empty | ||
| - Cannot start with +. | ||
| - Cannot be `.` or `..`. | ||
| - Cannot be longer than 255 bytes (note it is bytes, so multi-byte characters will count towards the 255 limit faster). | ||
| - Patterns starting with . are deprecated, except for hidden indices and internal indices managed by plugins. | ||
|""".stripMargin | ||
) | ||
) | ||
} | ||
} | ||
} | ||
type IndexPattern = IndexPattern.Type | ||
} | ||
|
||
final case class MultiIndex private (indices: Chunk[String]) { self => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it makes sense to make this |
||
def names(name: IndexName, names: IndexName*): MultiIndex = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put empty line between. |
||
self.copy(indices = indices ++ Chunk.fromIterable(name.toString +: names.map(IndexName.unwrap))) | ||
|
||
def patterns(pattern: IndexPattern, patterns: IndexPattern*): MultiIndex = | ||
self.copy(indices = indices ++ Chunk.fromIterable(pattern.toString +: patterns.map(IndexPattern.unwrap))) | ||
} | ||
|
||
object MultiIndex { | ||
def names(name: IndexName, names: IndexName*): MultiIndex = | ||
new MultiIndex(Chunk.fromIterable(name.toString +: names.map(IndexName.unwrap))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can omit new, as this is final case class. |
||
|
||
def patterns(pattern: IndexPattern, patterns: IndexPattern*): MultiIndex = | ||
new MultiIndex(Chunk.fromIterable(pattern.toString +: patterns.map(IndexPattern.unwrap))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can omit new, as this is final case class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe put this after this part
To create a GetById request do the following:
.