Skip to content

Commit c5d6ddc

Browse files
committed
Merge remote-tracking branch 'carlosdelest/feature/esql_dense_vector_support' into feature/esql_dense_vector_support
2 parents a63ca2c + fe82007 commit c5d6ddc

File tree

38 files changed

+3546
-284
lines changed

38 files changed

+3546
-284
lines changed

build-tools/src/integTest/groovy/org/elasticsearch/gradle/test/TestBuildInfoPluginFuncTest.groovy

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ import com.fasterxml.jackson.databind.ObjectMapper
55
import org.elasticsearch.gradle.fixtures.AbstractGradleFuncTest
66
import org.gradle.testkit.runner.TaskOutcome
77

8-
import java.nio.file.Path
9-
108
class TestBuildInfoPluginFuncTest extends AbstractGradleFuncTest {
11-
def "works"() {
9+
def "basic functionality"() {
1210
given:
1311
file("src/main/java/com/example/Example.java") << """
1412
package com.example;
@@ -54,12 +52,70 @@ class TestBuildInfoPluginFuncTest extends AbstractGradleFuncTest {
5452

5553
def location = Map.of(
5654
"module", "com.example",
57-
"representative_class", Path.of("com", "example", "Example.class").toString()
55+
"representative_class", "com/example/Example.class"
5856
)
5957
def expectedOutput = Map.of(
6058
"component", "example-component",
6159
"locations", List.of(location)
6260
)
6361
new ObjectMapper().readValue(output, Map.class) == expectedOutput
6462
}
63+
64+
def "dependencies"() {
65+
buildFile << """
66+
import org.elasticsearch.gradle.plugin.GenerateTestBuildInfoTask;
67+
68+
plugins {
69+
id 'java'
70+
id 'elasticsearch.test-build-info'
71+
}
72+
73+
repositories {
74+
mavenCentral()
75+
}
76+
77+
dependencies {
78+
// We pin to specific versions here because they are known to have the properties we want to test.
79+
// We're not actually running this code.
80+
implementation "org.ow2.asm:asm:9.7.1" // has module-info.class
81+
implementation "junit:junit:4.13" // has Automatic-Module-Name, and brings in hamcrest which does not
82+
}
83+
84+
tasks.withType(GenerateTestBuildInfoTask.class) {
85+
componentName = 'example-component'
86+
outputFile = new File('build/generated-build-info/plugin-test-build-info.json')
87+
}
88+
"""
89+
90+
when:
91+
def result = gradleRunner('generateTestBuildInfo').build()
92+
def task = result.task(":generateTestBuildInfo")
93+
94+
95+
then:
96+
task.outcome == TaskOutcome.SUCCESS
97+
98+
def output = file("build/generated-build-info/plugin-test-build-info.json")
99+
output.exists() == true
100+
101+
def locationFromModuleInfo = Map.of(
102+
"module", "org.objectweb.asm",
103+
"representative_class", 'org/objectweb/asm/AnnotationVisitor.class'
104+
)
105+
def locationFromManifest = Map.of(
106+
"module", "junit",
107+
"representative_class", 'junit/textui/TestRunner.class'
108+
)
109+
def locationFromJarFileName = Map.of(
110+
"module", "hamcrest.core",
111+
"representative_class", 'org/hamcrest/BaseDescription.class'
112+
)
113+
def expectedOutput = Map.of(
114+
"component", "example-component",
115+
"locations", List.of(locationFromModuleInfo, locationFromManifest, locationFromJarFileName)
116+
)
117+
118+
def value = new ObjectMapper().readValue(output, Map.class)
119+
value == expectedOutput
120+
}
65121
}

build-tools/src/main/java/org/elasticsearch/gradle/plugin/GenerateTestBuildInfoTask.java

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import java.nio.file.attribute.BasicFileAttributes;
4242
import java.security.CodeSource;
4343
import java.util.ArrayList;
44-
import java.util.Arrays;
44+
import java.util.Comparator;
4545
import java.util.List;
4646
import java.util.jar.JarEntry;
4747
import java.util.jar.JarFile;
@@ -209,17 +209,16 @@ private String extractModuleNameFromJar(File file, JarFile jarFile) throws IOExc
209209
* @return a {@link StringBuilder} with the {@code META-INF/versions/<version number>} if it exists; otherwise null
210210
*/
211211
private static StringBuilder versionDirectoryIfExists(JarFile jarFile) {
212+
Comparator<Integer> numericOrder = Integer::compareTo;
212213
List<Integer> versions = jarFile.stream()
213214
.filter(je -> je.getName().startsWith(META_INF_VERSIONS_PREFIX) && je.getName().endsWith("/module-info.class"))
214215
.map(
215216
je -> Integer.parseInt(
216217
je.getName().substring(META_INF_VERSIONS_PREFIX.length(), je.getName().length() - META_INF_VERSIONS_PREFIX.length())
217218
)
218219
)
220+
.sorted(numericOrder.reversed())
219221
.toList();
220-
versions = new ArrayList<>(versions);
221-
versions.sort(Integer::compareTo);
222-
versions = versions.reversed();
223222
int major = Runtime.version().feature();
224223
StringBuilder path = new StringBuilder(META_INF_VERSIONS_PREFIX);
225224
for (int version : versions) {
@@ -300,7 +299,10 @@ private String extractClassNameFromDirectory(File dir) throws IOException {
300299
public @NotNull FileVisitResult visitFile(@NotNull Path candidate, @NotNull BasicFileAttributes attrs) {
301300
String name = candidate.getFileName().toString(); // Just the part after the last dir separator
302301
if (name.endsWith(".class") && (name.equals("module-info.class") || name.contains("$")) == false) {
303-
result = candidate.toAbsolutePath().toString().substring(dir.getAbsolutePath().length() + 1);
302+
result = candidate.toAbsolutePath()
303+
.toString()
304+
.substring(dir.getAbsolutePath().length() + 1)
305+
.replace(File.separatorChar, '/');
304306
return TERMINATE;
305307
} else {
306308
return CONTINUE;
@@ -316,20 +318,24 @@ private String extractClassNameFromDirectory(File dir) throws IOException {
316318
* if it exists or the preset one derived from the jar task
317319
*/
318320
private String extractModuleNameFromDirectory(File dir) throws IOException {
319-
List<File> files = new ArrayList<>(List.of(dir));
320-
while (files.isEmpty() == false) {
321-
File find = files.removeFirst();
322-
if (find.exists()) {
323-
if (find.getName().equals("module-info.class")) {
324-
try (InputStream inputStream = new FileInputStream(find)) {
325-
return extractModuleNameFromModuleInfo(inputStream);
321+
var visitor = new SimpleFileVisitor<Path>() {
322+
private String result = getModuleName().getOrNull();
323+
324+
@Override
325+
public @NotNull FileVisitResult visitFile(@NotNull Path candidate, @NotNull BasicFileAttributes attrs) throws IOException {
326+
String name = candidate.getFileName().toString(); // Just the part after the last dir separator
327+
if (name.equals("module-info.class")) {
328+
try (InputStream inputStream = new FileInputStream(candidate.toFile())) {
329+
result = extractModuleNameFromModuleInfo(inputStream);
330+
return TERMINATE;
326331
}
327-
} else if (find.isDirectory()) {
328-
files.addAll(Arrays.asList(find.listFiles()));
332+
} else {
333+
return CONTINUE;
329334
}
330335
}
331-
}
332-
return getModuleName().getOrNull();
336+
};
337+
Files.walkFileTree(dir.toPath(), visitor);
338+
return visitor.result;
333339
}
334340

335341
/**

docs/changelog/128259.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 128259
2+
summary: Added geometry validation for GEO types to exit early on invalid latitudes
3+
area: Geo
4+
type: bug
5+
issues:
6+
- 128234

docs/changelog/128263.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 128263
2+
summary: Allow lookup join on mixed numeric fields
3+
area: ES|QL
4+
type: enhancement
5+
issues: []

docs/changelog/128320.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 128320
2+
summary: Use new source loader when lower `docId` is accessed
3+
area: Codec
4+
type: bug
5+
issues: []

muted-tests.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,24 @@ tests:
477477
- class: org.elasticsearch.reservedstate.service.FileSettingsServiceIT
478478
method: testSymlinkUpdateTriggerReload
479479
issue: https://github.com/elastic/elasticsearch/issues/128369
480+
- class: org.elasticsearch.xpack.esql.qa.single_node.PushQueriesIT
481+
method: testEqualityAndOther {semantic_text}
482+
issue: https://github.com/elastic/elasticsearch/issues/128414
483+
- class: org.elasticsearch.index.query.MultiMatchQueryBuilderTests
484+
method: testToQuery
485+
issue: https://github.com/elastic/elasticsearch/issues/128416
486+
- class: org.elasticsearch.xpack.ml.integration.InferenceIngestIT
487+
method: testPipelineIngestWithModelAliases
488+
issue: https://github.com/elastic/elasticsearch/issues/128417
489+
- class: org.elasticsearch.xpack.search.CrossClusterAsyncSearchIT
490+
method: testCCSClusterDetailsWhereAllShardsSkippedInCanMatch
491+
issue: https://github.com/elastic/elasticsearch/issues/128418
492+
- class: org.elasticsearch.xpack.esql.action.ForkIT
493+
method: testProfile
494+
issue: https://github.com/elastic/elasticsearch/issues/128377
495+
- class: org.elasticsearch.xpack.esql.action.CrossClusterQueryWithFiltersIT
496+
method: testTimestampFilterFromQuery
497+
issue: https://github.com/elastic/elasticsearch/issues/127332
480498

481499
# Examples:
482500
#

server/src/main/java/org/elasticsearch/search/lookup/ConcurrentSegmentSourceProvider.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ class ConcurrentSegmentSourceProvider implements SourceProvider {
2929
private final SourceLoader sourceLoader;
3030
private final StoredFieldLoader storedFieldLoader;
3131
private final Map<Object, Leaf> leaves = ConcurrentCollections.newConcurrentMap();
32+
private final boolean isStoredSource;
3233

33-
ConcurrentSegmentSourceProvider(SourceLoader loader, boolean loadSource) {
34+
ConcurrentSegmentSourceProvider(SourceLoader loader, boolean isStoredSource) {
3435
this.sourceLoader = loader;
3536
// we force a sequential reader here since it is used during query execution where documents are scanned sequentially
36-
this.storedFieldLoader = StoredFieldLoader.create(loadSource, sourceLoader.requiredStoredFields(), true);
37+
this.storedFieldLoader = StoredFieldLoader.create(isStoredSource, sourceLoader.requiredStoredFields(), true);
38+
this.isStoredSource = isStoredSource;
3739
}
3840

3941
@Override
@@ -44,6 +46,14 @@ public Source getSource(LeafReaderContext ctx, int doc) throws IOException {
4446
leaf = new Leaf(sourceLoader.leaf(ctx.reader(), null), storedFieldLoader.getLoader(ctx, null));
4547
var existing = leaves.put(id, leaf);
4648
assert existing == null : "unexpected source provider [" + existing + "]";
49+
} else if (isStoredSource == false && doc < leaf.doc) {
50+
// When queries reference the same runtime field in multiple clauses, each clause re-reads the values from the source in
51+
// increasing docId order. So the last docId accessed by the first clause is higher than the first docId read by the second
52+
// clause. This is okay for stored source, as stored fields do not restrict the order that docIds that can be accessed.
53+
// But with synthetic source, field values may come from doc values, which require than docIds only be read in increasing order.
54+
// To handle this, we detect lower docIds and create a new doc value reader for each clause.
55+
leaf = new Leaf(sourceLoader.leaf(ctx.reader(), null), storedFieldLoader.getLoader(ctx, null));
56+
leaves.put(id, leaf);
4757
}
4858
return leaf.getSource(ctx, doc);
4959
}

x-pack/plugin/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ tasks.named("yamlRestCompatTestTransform").configure({ task ->
116116
task.skipTest("esql/40_tsdb/from index pattern unsupported counter", "TODO: support for subset of metric fields")
117117
task.skipTest("esql/40_unsupported_types/unsupported", "TODO: support for subset of metric fields")
118118
task.skipTest("esql/40_unsupported_types/unsupported with sort", "TODO: support for subset of metric fields")
119+
task.skipTest("service_accounts/10_basic/Test get service accounts", "Enterprise Search service account removed, invalidating the current tests")
120+
task.skipTest("service_accounts/10_basic/Test service account tokens", "Enterprise Search service account removed, invalidating the current tests")
119121
task.skipTest("ml/3rd_party_deployment/Test clear deployment cache", "Deprecated route removed")
120122
task.skipTest("ml/3rd_party_deployment/Test start and stop deployment with cache", "Deprecated route removed")
121123
task.skipTest("ml/3rd_party_deployment/Test start and stop multiple deployments", "Deprecated route removed")

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/xcontent/XContentUtilsTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public void testAddAuthorizationInfoWithApiKey() throws IOException {
6363
}
6464

6565
public void testAddAuthorizationInfoWithServiceAccount() throws IOException {
66-
String account = "elastic/" + randomFrom("kibana", "fleet-server", "enterprise-search-server");
66+
String account = "elastic/" + randomFrom("kibana", "fleet-server");
6767
User user = new User(account);
6868
AuthenticationTestBuilder builder = AuthenticationTestHelper.builder().serviceAccount(user);
6969
Authentication authentication = builder.build();

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/util/SpatialCoordinateTypes.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.apache.lucene.util.BytesRef;
1313
import org.elasticsearch.geometry.Geometry;
1414
import org.elasticsearch.geometry.Point;
15+
import org.elasticsearch.geometry.utils.GeographyValidator;
1516
import org.elasticsearch.geometry.utils.GeometryValidator;
1617
import org.elasticsearch.geometry.utils.WellKnownBinary;
1718
import org.elasticsearch.geometry.utils.WellKnownText;
@@ -32,6 +33,11 @@ public long pointAsLong(double x, double y) {
3233
int longitudeEncoded = encodeLongitude(x);
3334
return (((long) latitudeEncoded) << 32) | (longitudeEncoded & 0xFFFFFFFFL);
3435
}
36+
37+
public GeometryValidator validator() {
38+
// We validate the lat/lon values, and ignore any z values
39+
return GeographyValidator.instance(true);
40+
}
3541
},
3642
CARTESIAN {
3743

@@ -67,6 +73,10 @@ public long pointAsLong(double x, double y) {
6773
}
6874
};
6975

76+
protected GeometryValidator validator() {
77+
return GeometryValidator.NOOP;
78+
}
79+
7080
public abstract Point longAsPoint(long encoded);
7181

7282
public abstract long pointAsLong(double x, double y);
@@ -77,7 +87,7 @@ public long wkbAsLong(BytesRef wkb) {
7787
}
7888

7989
public Point wkbAsPoint(BytesRef wkb) {
80-
Geometry geometry = WellKnownBinary.fromWKB(GeometryValidator.NOOP, false, wkb.bytes, wkb.offset, wkb.length);
90+
Geometry geometry = WellKnownBinary.fromWKB(validator(), false, wkb.bytes, wkb.offset, wkb.length);
8191
if (geometry instanceof Point point) {
8292
return point;
8393
} else {
@@ -101,26 +111,18 @@ public BytesRef wktToWkb(String wkt) {
101111
// TODO: we should be able to transform WKT to WKB without building the geometry
102112
// we should as well use different validator for cartesian and geo?
103113
try {
104-
Geometry geometry = WellKnownText.fromWKT(GeometryValidator.NOOP, false, wkt);
114+
Geometry geometry = WellKnownText.fromWKT(validator(), false, wkt);
105115
return new BytesRef(WellKnownBinary.toWKB(geometry, ByteOrder.LITTLE_ENDIAN));
106116
} catch (Exception e) {
107117
throw new IllegalArgumentException("Failed to parse WKT: " + e.getMessage(), e);
108118
}
109119
}
110120

111-
public Geometry wktToGeometry(String wkt) {
112-
try {
113-
return WellKnownText.fromWKT(GeometryValidator.NOOP, false, wkt);
114-
} catch (Exception e) {
115-
throw new IllegalArgumentException("Failed to parse WKT: " + e.getMessage(), e);
116-
}
117-
}
118-
119121
public String wkbToWkt(BytesRef wkb) {
120122
return WellKnownText.fromWKB(wkb.bytes, wkb.offset, wkb.length);
121123
}
122124

123125
public Geometry wkbToGeometry(BytesRef wkb) {
124-
return WellKnownBinary.fromWKB(GeometryValidator.NOOP, false, wkb.bytes, wkb.offset, wkb.length);
126+
return WellKnownBinary.fromWKB(validator(), false, wkb.bytes, wkb.offset, wkb.length);
125127
}
126128
}

0 commit comments

Comments
 (0)