summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cloud-tenant-base-dependencies-enforcer/pom.xml2
-rwxr-xr-xconfig-proxy/src/main/sh/vespa-config-ctl.sh14
-rw-r--r--config_test/.gitignore2
-rw-r--r--config_test/OWNERS1
-rw-r--r--config_test/README.md3
-rw-r--r--config_test/pom.xml55
-rw-r--r--config_test/src/main/java/com/yahoo/configtest/Demo.java14
-rw-r--r--config_test/src/main/resources/configdefinitions/configtest.greeting.def10
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java22
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/FileDistributionMaintainer.java2
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java31
-rw-r--r--container-test/pom.xml5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java3
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java11
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java46
-rw-r--r--parent/pom.xml6
-rw-r--r--pom.xml1
-rw-r--r--searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp2
-rw-r--r--searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp172
-rw-r--r--searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp349
-rw-r--r--searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp20
-rw-r--r--searchlib/src/tests/tensor/hnsw_saver/hnsw_save_load_test.cpp34
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/blueprint.cpp100
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/blueprint.h21
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.cpp11
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.h8
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/equiv_blueprint.cpp8
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp96
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h16
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.cpp14
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/simple_phrase_blueprint.cpp23
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/simple_phrase_blueprint.h8
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.cpp17
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h2
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.cpp17
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.h12
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/weighted_set_term_search.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/weighted_set_term_search.h2
-rw-r--r--searchlib/src/vespa/searchlib/tensor/bitvector_visited_tracker.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/tensor/bitvector_visited_tracker.h10
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hash_set_visited_tracker.h6
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_graph.cpp42
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_graph.h54
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp224
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index.h34
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.h4
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.hpp18
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.cpp6
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.h2
-rw-r--r--searchlib/src/vespa/searchlib/tensor/hnsw_index_utils.h10
-rw-r--r--vespalog/src/logger/runserver.cpp14
53 files changed, 870 insertions, 731 deletions
diff --git a/cloud-tenant-base-dependencies-enforcer/pom.xml b/cloud-tenant-base-dependencies-enforcer/pom.xml
index 5dfded1de09..03f14d7d65f 100644
--- a/cloud-tenant-base-dependencies-enforcer/pom.xml
+++ b/cloud-tenant-base-dependencies-enforcer/pom.xml
@@ -46,6 +46,7 @@
<jetty.version>9.4.49.v20220914</jetty.version>
<jetty-alpn.version>1.1.3.v20160715</jetty-alpn.version>
<org.lz4.version>1.8.0</org.lz4.version>
+ <org.json.version>20220320</org.json.version> <!-- TODO: Remove on Vespa 9 -->
<slf4j.version>1.7.32</slf4j.version> <!-- WARNING: when updated, also update c.y.v.tenant:base pom -->
<xml-apis.version>1.4.01</xml-apis.version>
</properties>
@@ -218,6 +219,7 @@
<include>org.eclipse.jetty:jetty-util:[${jetty.version}]:jar:test</include>
<include>org.hamcrest:hamcrest-core:1.3:jar:test</include>
<include>org.hdrhistogram:HdrHistogram:2.1.8:jar:test</include>
+ <include>org.json:json:${org.json.version}:jar:test</include>
<include>org.junit.jupiter:junit-jupiter-api:[${junit5.version}]:jar:test</include>
<include>org.junit.jupiter:junit-jupiter-engine:[${junit5.version}]:jar:test</include>
<include>org.junit.platform:junit-platform-commons:[${junit5.platform.version}]:jar:test</include>
diff --git a/config-proxy/src/main/sh/vespa-config-ctl.sh b/config-proxy/src/main/sh/vespa-config-ctl.sh
index e02073787db..63aaf11280f 100755
--- a/config-proxy/src/main/sh/vespa-config-ctl.sh
+++ b/config-proxy/src/main/sh/vespa-config-ctl.sh
@@ -88,7 +88,7 @@ LOGDIR="${VESPA_HOME}/logs/vespa"
LOGFILE="$LOGDIR/vespa.log"
VESPA_LOG_TARGET="file:$LOGFILE"
VESPA_LOG_CONTROL_DIR="${VESPA_HOME}/var/db/vespa/logcontrol"
-cp="libexec/vespa/patches/configproxy:lib/jars/config-proxy-jar-with-dependencies.jar"
+cp="lib/jars/config-proxy-jar-with-dependencies.jar"
VESPA_LOG_LEVEL="all -debug -spam"
@@ -120,12 +120,12 @@ case $1 in
echo "Starting config proxy using $configsources as config source(s)"
vespa-runserver -r 10 -s configproxy -p $P_CONFIG_PROXY -- \
java ${jvmopts} \
- -XX:+ExitOnOutOfMemoryError $(getJavaOptionsIPV46) \
- -Dproxyconfigsources="${configsources}" \
- -Djava.io.tmpdir=${VESPA_HOME}/tmp \
- ${userargs} \
- -XX:ActiveProcessorCount=2 \
- -cp $cp com.yahoo.vespa.config.proxy.ProxyServer 19090
+ -XX:+ExitOnOutOfMemoryError $(getJavaOptionsIPV46) \
+ -Dproxyconfigsources="${configsources}" \
+ -Djava.io.tmpdir=${VESPA_HOME}/tmp \
+ ${userargs} \
+ -XX:ActiveProcessorCount=2 \
+ -cp $cp com.yahoo.vespa.config.proxy.ProxyServer 19090
echo "Waiting for config proxy to start"
fail=true
diff --git a/config_test/.gitignore b/config_test/.gitignore
deleted file mode 100644
index 3cc25b51fc4..00000000000
--- a/config_test/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/pom.xml.build
-/target
diff --git a/config_test/OWNERS b/config_test/OWNERS
deleted file mode 100644
index 338ed581212..00000000000
--- a/config_test/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-hmusum
diff --git a/config_test/README.md b/config_test/README.md
deleted file mode 100644
index 7ff633d935f..00000000000
--- a/config_test/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-# Test using cloud config without Vespa
-
diff --git a/config_test/pom.xml b/config_test/pom.xml
deleted file mode 100644
index d2c8abf42ff..00000000000
--- a/config_test/pom.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0"?>
-<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config_test</artifactId>
- <packaging>container-plugin</packaging>
- <version>8-SNAPSHOT</version>
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.13.2</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config-lib</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.8.1</version>
- <configuration>
- <source>1.8</source>
- <target>1.8</target>
- <compilerArgs>
- <arg>-Xlint:all</arg>
- <arg>-Werror</arg>
- </compilerArgs>
- </configuration>
- </plugin>
- <plugin>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>bundle-plugin</artifactId>
- <version>${project.version}</version>
- <extensions>true</extensions>
- <configuration>
- <configGenVersion>${project.version}</configGenVersion>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <properties>
- <maven.deploy.skip>true</maven.deploy.skip>
- <maven.javadoc.skip>true</maven.javadoc.skip>
- <skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- </properties>
-</project>
diff --git a/config_test/src/main/java/com/yahoo/configtest/Demo.java b/config_test/src/main/java/com/yahoo/configtest/Demo.java
deleted file mode 100644
index 792fffa36a9..00000000000
--- a/config_test/src/main/java/com/yahoo/configtest/Demo.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.configtest;
-
-/**
- * Just check we manage to compile something that requires configuration and
- * has no dependencies to Vespa.
- */
-public class Demo {
- public final String greeting;
-
- public Demo(GreetingConfig config) {
- greeting = config.greeting();
- }
-}
diff --git a/config_test/src/main/resources/configdefinitions/configtest.greeting.def b/config_test/src/main/resources/configdefinitions/configtest.greeting.def
deleted file mode 100644
index 901362ae386..00000000000
--- a/config_test/src/main/resources/configdefinitions/configtest.greeting.def
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-namespace=configtest
-
-greeting string default="Hello, world."
-arbitrary double
-
-values[].marker int
-values[].operations[].type enum { NALLE, BAMSE, BRAKAR }
-values[].operations[].arguments[].key string
-values[].operations[].arguments[].value string
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
index cf79005b1ab..cd61b44be1b 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java
@@ -597,14 +597,15 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
public List<String> deleteUnusedFileDistributionReferences(File fileReferencesPath,
Duration keepFileReferencesDuration,
int numberToAlwaysKeep) {
- log.log(Level.FINE, () -> "Keep unused file references for " + keepFileReferencesDuration);
if (!fileReferencesPath.isDirectory()) throw new RuntimeException(fileReferencesPath + " is not a directory");
Set<String> fileReferencesInUse = getFileReferencesInUse();
log.log(Level.FINE, () -> "File references in use : " + fileReferencesInUse);
+ Instant instant = clock.instant().minus(keepFileReferencesDuration);
+ log.log(Level.FINE, () -> "Remove unused file references last modified before " + instant +
+ " (but keep " + numberToAlwaysKeep + " of those)");
- List<String> candidates = sortedUnusedFileReferences(fileReferencesPath, fileReferencesInUse, keepFileReferencesDuration);
- // Do not delete the newest ones
+ List<String> candidates = sortedUnusedFileReferences(fileReferencesPath, fileReferencesInUse, instant);
List<String> fileReferencesToDelete = candidates.subList(0, Math.max(0, candidates.size() - numberToAlwaysKeep));
if (fileReferencesToDelete.size() > 0) {
log.log(Level.FINE, () -> "Will delete file references not in use: " + fileReferencesToDelete);
@@ -628,15 +629,14 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
return fileReferencesInUse;
}
- private List<String> sortedUnusedFileReferences(File fileReferencesPath, Set<String> fileReferencesInUse, Duration keepFileReferences) {
+ private List<String> sortedUnusedFileReferences(File fileReferencesPath, Set<String> fileReferencesInUse, Instant instant) {
Set<String> fileReferencesOnDisk = getFileReferencesOnDisk(fileReferencesPath);
log.log(Level.FINE, () -> "File references on disk (in " + fileReferencesPath + "): " + fileReferencesOnDisk);
- Instant instant = clock.instant().minus(keepFileReferences);
return fileReferencesOnDisk
.stream()
.filter(fileReference -> ! fileReferencesInUse.contains(fileReference))
- .filter(fileReference -> isLastFileAccessBefore(new File(fileReferencesPath, fileReference), instant))
- .sorted(Comparator.comparing(a -> lastAccessed(new File(fileReferencesPath, a))))
+ .filter(fileReference -> isLastModifiedBefore(new File(fileReferencesPath, fileReference), instant))
+ .sorted(Comparator.comparing(a -> lastModified(new File(fileReferencesPath, a))))
.collect(Collectors.toList());
}
@@ -690,15 +690,15 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye
.collect(Collectors.toList());
}
- private boolean isLastFileAccessBefore(File fileReference, Instant instant) {
- return lastAccessed(fileReference).isBefore(instant);
+ private boolean isLastModifiedBefore(File fileReference, Instant instant) {
+ return lastModified(fileReference).isBefore(instant);
}
- private Instant lastAccessed(File fileReference) {
+ private Instant lastModified(File fileReference) {
BasicFileAttributes fileAttributes;
try {
fileAttributes = readAttributes(fileReference.toPath(), BasicFileAttributes.class);
- return fileAttributes.lastAccessTime().toInstant();
+ return fileAttributes.lastModifiedTime().toInstant();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/FileDistributionMaintainer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/FileDistributionMaintainer.java
index f6aee416c9c..0588a126b68 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/FileDistributionMaintainer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/maintenance/FileDistributionMaintainer.java
@@ -20,7 +20,7 @@ import java.time.Duration;
*/
public class FileDistributionMaintainer extends ConfigServerMaintainer {
- private static final int numberToAlwaysKeep = 20;
+ private static final int numberToAlwaysKeep = 10; // TODO: Reduce to 0 / remove
private final ApplicationRepository applicationRepository;
private final File fileReferencesDir;
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
index b185bb5915d..e8d969119f9 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ApplicationRepositoryTest.java
@@ -61,7 +61,6 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
-import java.nio.file.attribute.FileTime;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@@ -266,13 +265,13 @@ public class ApplicationRepositoryTest {
Duration keepFileReferencesDuration = Duration.ofSeconds(4);
// Add file reference that is not in use and should be deleted (older than 'keepFileReferencesDuration')
- File filereferenceDirOldest = createFilereferenceOnDisk(new File(fileReferencesDir, "foo"));
+ File filereferenceDirOldest = createFileReferenceOnDisk(new File(fileReferencesDir, "bar"));
clock.advance(Duration.ofSeconds(1));
// Add file references that are not in use and could be deleted
IntStream.range(0, 3).forEach(i -> {
try {
- createFilereferenceOnDisk(new File(fileReferencesDir, "bar" + i));
+ createFileReferenceOnDisk(new File(fileReferencesDir, "baz" + i));
} catch (IOException e) {
fail(e.getMessage());
}
@@ -281,7 +280,7 @@ public class ApplicationRepositoryTest {
clock.advance(keepFileReferencesDuration);
// Add file reference that is not in use, but should not be deleted (newer than 'keepFileReferencesDuration')
- File filereferenceDirNewest = createFilereferenceOnDisk(new File(fileReferencesDir, "baz"));
+ File filereferenceDirNewest = createFileReferenceOnDisk(new File(fileReferencesDir, "foo"));
applicationRepository = new ApplicationRepository.Builder()
.withTenantRepository(tenantRepository)
@@ -294,23 +293,25 @@ public class ApplicationRepositoryTest {
PrepareParams prepareParams = new PrepareParams.Builder().applicationId(applicationId()).ignoreValidationErrors(true).build();
deployApp(new File("src/test/apps/app"), prepareParams);
- List<String> toBeDeleted = applicationRepository.deleteUnusedFileDistributionReferences(fileReferencesDir,
+ List<String> deleted = applicationRepository.deleteUnusedFileDistributionReferences(fileReferencesDir,
keepFileReferencesDuration,
2);
- Collections.sort(toBeDeleted);
- assertEquals(List.of("bar0", "foo"), toBeDeleted);
- // bar0 and foo are the only ones that will be deleted (keeps 2 newest no matter how old they are)
+ Collections.sort(deleted);
+ List<String> expected = new ArrayList<>(List.of("bar", "baz0"));
+ Collections.sort(expected);
+ assertEquals(expected, deleted);
+ // bar and baz0 will be deleted, 2 of the old ones (baz1 and baz2) will be kept and foo is not old enough to be considered
assertFalse(filereferenceDirOldest.exists());
- assertFalse(new File(fileReferencesDir, "bar0").exists());
+ assertFalse(new File(fileReferencesDir, "baz0").exists());
assertTrue(filereferenceDirNewest.exists());
}
- private File createFilereferenceOnDisk(File filereferenceDir) throws IOException {
- assertTrue(filereferenceDir.mkdir());
- File file = new File(filereferenceDir, "bar");
- IOUtils.writeFile(file, Utf8.toBytes("test"));
- Files.setAttribute(filereferenceDir.toPath(), "lastAccessTime", FileTime.from(clock.instant()));
- return filereferenceDir;
+ private File createFileReferenceOnDisk(File filereference) throws IOException {
+ File fileReferenceDir = filereference.getParentFile();
+ fileReferenceDir.mkdir();
+ IOUtils.writeFile(filereference, Utf8.toBytes("test"));
+ filereference.setLastModified(clock.instant().toEpochMilli());
+ return filereference;
}
@Test
diff --git a/container-test/pom.xml b/container-test/pom.xml
index 2b1b20a9ed0..32a64a98b9e 100644
--- a/container-test/pom.xml
+++ b/container-test/pom.xml
@@ -100,6 +100,11 @@
<artifactId>commons-compress</artifactId>
<scope>compile</scope>
</dependency>
+ <dependency> <!-- TODO: Remove on Vespa 9 -->
+ <!-- not used by Vespa, but was historically on test classpath -->
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ </dependency>
<dependency>
<!-- TODO: this, and probably others, could be removed from here if we make the fat jar the default artifact for jdisc_core -->
<groupId>org.lz4</groupId>
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java
index 0fe9a84f5fa..c09fff19d58 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java
@@ -378,7 +378,8 @@ public class DeploymentStatus {
/** The set of jobs that need to run for the given changes to be considered complete. */
public boolean hasCompleted(InstanceName instance, Change change) {
- if ( ! application.deploymentSpec().requireInstance(instance).concerns(prod)) {
+ DeploymentInstanceSpec spec = application.deploymentSpec().requireInstance(instance);
+ if ((spec.concerns(test) || spec.concerns(staging)) && ! spec.concerns(prod)) {
if (newestTested(instance, run -> run.versions().targetRevision()).map(change::downgrades).orElse(false)) return true;
if (newestTested(instance, run -> run.versions().targetPlatform()).map(change::downgrades).orElse(false)) return true;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
index 6d1e5cebe0e..d9726edc496 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
@@ -1291,6 +1291,9 @@ public class DeploymentTriggerTest {
String complicatedDeploymentSpec =
"""
<deployment version='1.0' athenz-domain='domain' athenz-service='service'>
+ <instance id='dev'>
+ <dev />
+ </instance>
<parallel>
<instance id='instance' athenz-service='in-service'>
<staging />
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java b/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java
index 7766faf28c7..f68f6816ec8 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java
@@ -69,6 +69,12 @@ public class PermanentFlags {
"node resources of the host, the maximum number of containers, etc.",
"Takes effect on next iteration of DynamicProvisioningMaintainer.");
+ public static final UnboundStringFlag HOST_FLAVOR = defineStringFlag(
+ "host-flavor", "",
+ "Specifies the Vespa flavor name that the hosts of the matching nodes should have.",
+ "Takes effect on next deployment (including internal redeployment).",
+ APPLICATION_ID, CLUSTER_TYPE);
+
public static final UnboundBooleanFlag SKIP_MAINTENANCE_DEPLOYMENT = defineFeatureFlag(
"node-repository-skip-maintenance-deployment", false,
"Whether PeriodicApplicationMaintainer should skip deployment for an application",
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
index 9557354725b..ff24f627aa4 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
@@ -9,6 +9,8 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.SystemName;
import com.yahoo.net.HostName;
+import com.yahoo.vespa.flags.FetchVector;
+import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
@@ -81,6 +83,7 @@ class NodeAllocation {
private final NodeRepository nodeRepository;
private final NodeResourceLimits nodeResourceLimits;
+ private final Optional<String> requiredHostFlavor;
NodeAllocation(NodesAndHosts<? extends NodeList> allNodesAndHosts, ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes,
Supplier<Integer> nextIndex, NodeRepository nodeRepository) {
@@ -90,7 +93,12 @@ class NodeAllocation {
this.requestedNodes = requestedNodes;
this.nextIndex = nextIndex;
this.nodeRepository = nodeRepository;
- nodeResourceLimits = new NodeResourceLimits(nodeRepository);
+ this.nodeResourceLimits = new NodeResourceLimits(nodeRepository);
+ this.requiredHostFlavor = Optional.of(PermanentFlags.HOST_FLAVOR.bindTo(nodeRepository.flagSource())
+ .with(FetchVector.Dimension.APPLICATION_ID, application.serializedForm())
+ .with(FetchVector.Dimension.CLUSTER_TYPE, cluster.type().name())
+ .value())
+ .filter(s -> !s.isBlank());
}
/**
@@ -115,6 +123,7 @@ class NodeAllocation {
if ( candidate.state() == Node.State.active && allocation.removable()) continue; // don't accept; causes removal
if ( candidate.state() == Node.State.active && candidate.wantToFail()) continue; // don't accept; causes failing
if ( indexes.contains(membership.index())) continue; // duplicate index (just to be sure)
+ if ( requiredHostFlavor.isPresent() && ! candidate.parent.map(node -> node.flavor().name()).equals(requiredHostFlavor)) continue;
boolean resizeable = requestedNodes.considerRetiring() && candidate.isResizable;
boolean acceptToRetire = acceptToRetire(candidate);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
index 3570e56e196..94b525cfe3c 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
@@ -11,12 +11,15 @@ import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.NodeResources;
+import com.yahoo.config.provision.NodeResources.Architecture;
import com.yahoo.config.provision.NodeResources.DiskSpeed;
import com.yahoo.config.provision.NodeResources.StorageType;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
+import com.yahoo.vespa.flags.InMemoryFlagSource;
+import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.node.Agent;
@@ -260,6 +263,49 @@ public class DynamicProvisioningTest {
}
@Test
+ public void migrates_nodes_on_host_flavor_flag_change() {
+ InMemoryFlagSource flagSource = new InMemoryFlagSource();
+ List<Flavor> flavors = List.of(new Flavor("x86", new NodeResources(1, 4, 50, 0.1, fast, local, Architecture.x86_64)),
+ new Flavor("arm", new NodeResources(1, 4, 50, 0.1, fast, local, Architecture.arm64)));
+ MockHostProvisioner hostProvisioner = new MockHostProvisioner(flavors);
+ ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone)
+ .flavors(flavors)
+ .hostProvisioner(hostProvisioner)
+ .resourcesCalculator(0, 0)
+ .nameResolver(nameResolver)
+ .flagSource(flagSource)
+ .build();
+
+ ApplicationId app = ProvisioningTester.applicationId();
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("8").build();
+ Capacity capacity = Capacity.from(new ClusterResources(4, 2, new NodeResources(1, 4, 50, 0.1, DiskSpeed.any, StorageType.any, Architecture.any)));
+
+ hostProvisioner.overrideHostFlavor("x86");
+ tester.activate(app, cluster, capacity);
+ NodeList nodes = tester.nodeRepository().nodes().list();
+ nodes.forEach(n -> System.out.println(n.hostname() + " " + n.flavor().name()));
+ assertEquals(4, nodes.owner(app).state(Node.State.active).size());
+ assertEquals(Set.of("x86"), nodes.parentsOf(nodes.owner(app).state(Node.State.active)).stream().map(n -> n.flavor().name()).collect(Collectors.toSet()));
+
+ hostProvisioner.overrideHostFlavor("arm");
+ flagSource.withStringFlag(PermanentFlags.HOST_FLAVOR.id(), "arm");
+ tester.activate(app, cluster, capacity);
+ nodes = tester.nodeRepository().nodes().list();
+ assertEquals(4, nodes.owner(app).state(Node.State.inactive).size());
+ assertEquals(4, nodes.owner(app).state(Node.State.active).size());
+ assertEquals(Set.of("x86"), nodes.parentsOf(tester.getNodes(app, Node.State.inactive)).stream().map(n -> n.flavor().name()).collect(Collectors.toSet()));
+ assertEquals(Set.of("arm"), nodes.parentsOf(tester.getNodes(app, Node.State.active)).stream().map(n -> n.flavor().name()).collect(Collectors.toSet()));
+
+ flagSource.removeFlag(PermanentFlags.HOST_FLAVOR.id()); // Resetting flag does not moves the nodes back
+ tester.activate(app, cluster, capacity);
+ nodes = tester.nodeRepository().nodes().list();
+ assertEquals(4, nodes.owner(app).state(Node.State.inactive).size());
+ assertEquals(4, nodes.owner(app).state(Node.State.active).size());
+ assertEquals(Set.of("x86"), nodes.parentsOf(tester.getNodes(app, Node.State.inactive)).stream().map(n -> n.flavor().name()).collect(Collectors.toSet()));
+ assertEquals(Set.of("arm"), nodes.parentsOf(tester.getNodes(app, Node.State.active)).stream().map(n -> n.flavor().name()).collect(Collectors.toSet()));
+ }
+
+ @Test
public void test_changing_limits() {
int memoryTax = 3;
List<Flavor> flavors = List.of(new Flavor("1x", new NodeResources(1, 10 - memoryTax, 100, 0.1, fast, remote)),
diff --git a/parent/pom.xml b/parent/pom.xml
index 57c718ee4f5..62d57660bcf 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -994,6 +994,11 @@
<artifactId>xercesImpl</artifactId>
<version>2.12.2</version>
</dependency>
+ <dependency> <!-- TODO: Remove on Vespa 9 -->
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>${org.json.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
@@ -1052,6 +1057,7 @@
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
<mockito.version>4.0.0</mockito.version>
<onnxruntime.version>1.12.1</onnxruntime.version> <!-- WARNING: sync cloud-tenant-base-dependencies-enforcer/pom.xml -->
+ <org.json.version>20220320</org.json.version>
<org.lz4.version>1.8.0</org.lz4.version>
<prometheus.client.version>0.6.0</prometheus.client.version>
<protobuf.version>3.21.7</protobuf.version>
diff --git a/pom.xml b/pom.xml
index a4cb9fa0a3f..de19e9585c0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,7 +50,6 @@
<module>config-proxy</module>
<module>configserver</module>
<module>configserver-flags</module>
- <module>config_test</module>
<module>container</module>
<module>container-apache-http-client-bundle</module>
<module>container-core</module>
diff --git a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp
index 8e8327584a7..3c0b49da762 100644
--- a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp
+++ b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp
@@ -30,7 +30,7 @@ public:
return mixChildrenFields();
}
- virtual void sort(std::vector<Blueprint*> &children) const override {
+ virtual void sort(Children &children) const override {
std::sort(children.begin(), children.end(), TieredGreaterEstimate());
}
diff --git a/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp b/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp
index 2379213b87b..ef0fd56840a 100644
--- a/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp
+++ b/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp
@@ -56,6 +56,18 @@ bool got_global_filter(Blueprint &b) {
return (static_cast<MyLeaf &>(b)).got_global_filter();
}
+void check_sort_order(IntermediateBlueprint &self, std::vector<Blueprint*> unordered, std::vector<size_t> order) {
+ ASSERT_EQUAL(unordered.size(), order.size());
+ std::vector<Blueprint::UP> children;
+ for (auto *child: unordered) {
+ children.push_back(std::unique_ptr<Blueprint>(child));
+ }
+ self.sort(children);
+ for (size_t i = 0; i < children.size(); ++i) {
+ EXPECT_EQUAL(children[i].get(), unordered[order[i]]);
+ }
+}
+
TEST("test AndNot Blueprint") {
AndNotBlueprint b;
{ // combine
@@ -86,20 +98,12 @@ TEST("test AndNot Blueprint") {
EXPECT_EQUAL(true, got_global_filter(a.getChild(1)));
}
{
- std::vector<Blueprint *> children;
- Blueprint::UP c1 = ap(MyLeafSpec(10).create());
- Blueprint::UP c2 = ap(MyLeafSpec(20).create());
- Blueprint::UP c3 = ap(MyLeafSpec(40).create());
- Blueprint::UP c4 = ap(MyLeafSpec(30).create());
- children.push_back(c1.get());
- children.push_back(c2.get());
- children.push_back(c3.get());
- children.push_back(c4.get());
- b.sort(children);
- EXPECT_EQUAL(c1.get(), children[0]);
- EXPECT_EQUAL(c3.get(), children[1]);
- EXPECT_EQUAL(c4.get(), children[2]);
- EXPECT_EQUAL(c2.get(), children[3]);
+ check_sort_order(b,
+ {MyLeafSpec(10).create(),
+ MyLeafSpec(20).create(),
+ MyLeafSpec(40).create(),
+ MyLeafSpec(30).create()},
+ {0, 2, 3, 1});
}
{
EXPECT_EQUAL(true, b.inheritStrict(0));
@@ -161,20 +165,12 @@ TEST("test And Blueprint") {
EXPECT_EQUAL(true, got_global_filter(a.getChild(1)));
}
{
- std::vector<Blueprint *> children;
- Blueprint::UP c1 = ap(MyLeafSpec(20).create());
- Blueprint::UP c2 = ap(MyLeafSpec(40).create());
- Blueprint::UP c3 = ap(MyLeafSpec(10).create());
- Blueprint::UP c4 = ap(MyLeafSpec(30).create());
- children.push_back(c1.get());
- children.push_back(c2.get());
- children.push_back(c3.get());
- children.push_back(c4.get());
- b.sort(children);
- EXPECT_EQUAL(c3.get(), children[0]);
- EXPECT_EQUAL(c1.get(), children[1]);
- EXPECT_EQUAL(c4.get(), children[2]);
- EXPECT_EQUAL(c2.get(), children[3]);
+ check_sort_order(b,
+ {MyLeafSpec(20).create(),
+ MyLeafSpec(40).create(),
+ MyLeafSpec(10).create(),
+ MyLeafSpec(30).create()},
+ {2, 0, 3, 1});
}
{
EXPECT_EQUAL(true, b.inheritStrict(0));
@@ -241,20 +237,12 @@ TEST("test Or Blueprint") {
EXPECT_EQUAL(true, got_global_filter(o.getChild(o.childCnt() - 1)));
}
{
- std::vector<Blueprint *> children;
- Blueprint::UP c1 = ap(MyLeafSpec(10).create());
- Blueprint::UP c2 = ap(MyLeafSpec(20).create());
- Blueprint::UP c3 = ap(MyLeafSpec(40).create());
- Blueprint::UP c4 = ap(MyLeafSpec(30).create());
- children.push_back(c1.get());
- children.push_back(c2.get());
- children.push_back(c3.get());
- children.push_back(c4.get());
- b.sort(children);
- EXPECT_EQUAL(c3.get(), children[0]);
- EXPECT_EQUAL(c4.get(), children[1]);
- EXPECT_EQUAL(c2.get(), children[2]);
- EXPECT_EQUAL(c1.get(), children[3]);
+ check_sort_order(b,
+ {MyLeafSpec(10).create(),
+ MyLeafSpec(20).create(),
+ MyLeafSpec(40).create(),
+ MyLeafSpec(30).create()},
+ {2, 3, 1, 0});
}
{
EXPECT_EQUAL(true, b.inheritStrict(0));
@@ -290,20 +278,12 @@ TEST("test Near Blueprint") {
EXPECT_EQUAL(0u, a.exposeFields().size());
}
{
- std::vector<Blueprint *> children;
- Blueprint::UP c1 = ap(MyLeafSpec(40).create());
- Blueprint::UP c2 = ap(MyLeafSpec(10).create());
- Blueprint::UP c3 = ap(MyLeafSpec(30).create());
- Blueprint::UP c4 = ap(MyLeafSpec(20).create());
- children.push_back(c1.get());
- children.push_back(c2.get());
- children.push_back(c3.get());
- children.push_back(c4.get());
- b.sort(children);
- EXPECT_EQUAL(c2.get(), children[0]);
- EXPECT_EQUAL(c4.get(), children[1]);
- EXPECT_EQUAL(c3.get(), children[2]);
- EXPECT_EQUAL(c1.get(), children[3]);
+ check_sort_order(b,
+ {MyLeafSpec(40).create(),
+ MyLeafSpec(10).create(),
+ MyLeafSpec(30).create(),
+ MyLeafSpec(20).create()},
+ {1, 3, 2, 0});
}
{
EXPECT_EQUAL(true, b.inheritStrict(0));
@@ -339,20 +319,12 @@ TEST("test ONear Blueprint") {
EXPECT_EQUAL(0u, a.exposeFields().size());
}
{
- std::vector<Blueprint *> children;
- Blueprint::UP c1 = ap(MyLeafSpec(20).create());
- Blueprint::UP c2 = ap(MyLeafSpec(10).create());
- Blueprint::UP c3 = ap(MyLeafSpec(40).create());
- Blueprint::UP c4 = ap(MyLeafSpec(30).create());
- children.push_back(c1.get());
- children.push_back(c2.get());
- children.push_back(c3.get());
- children.push_back(c4.get());
- b.sort(children);
- EXPECT_EQUAL(c1.get(), children[0]);
- EXPECT_EQUAL(c2.get(), children[1]);
- EXPECT_EQUAL(c3.get(), children[2]);
- EXPECT_EQUAL(c4.get(), children[3]);
+ check_sort_order(b,
+ {MyLeafSpec(20).create(),
+ MyLeafSpec(10).create(),
+ MyLeafSpec(40).create(),
+ MyLeafSpec(30).create()},
+ {0, 1, 2, 3});
}
{
EXPECT_EQUAL(true, b.inheritStrict(0));
@@ -396,20 +368,12 @@ TEST("test Rank Blueprint") {
EXPECT_EQUAL(true, got_global_filter(a.getChild(1)));
}
{
- std::vector<Blueprint *> children;
- Blueprint::UP c1 = ap(MyLeafSpec(20).create());
- Blueprint::UP c2 = ap(MyLeafSpec(10).create());
- Blueprint::UP c3 = ap(MyLeafSpec(40).create());
- Blueprint::UP c4 = ap(MyLeafSpec(30).create());
- children.push_back(c1.get());
- children.push_back(c2.get());
- children.push_back(c3.get());
- children.push_back(c4.get());
- b.sort(children);
- EXPECT_EQUAL(c1.get(), children[0]);
- EXPECT_EQUAL(c2.get(), children[1]);
- EXPECT_EQUAL(c3.get(), children[2]);
- EXPECT_EQUAL(c4.get(), children[3]);
+ check_sort_order(b,
+ {MyLeafSpec(20).create(),
+ MyLeafSpec(10).create(),
+ MyLeafSpec(40).create(),
+ MyLeafSpec(30).create()},
+ {0, 1, 2, 3});
}
{
EXPECT_EQUAL(true, b.inheritStrict(0));
@@ -469,20 +433,12 @@ TEST("test SourceBlender Blueprint") {
EXPECT_EQUAL(0u, a->getState().numFields());
}
{
- std::vector<Blueprint *> children;
- Blueprint::UP c1 = ap(MyLeafSpec(20).create());
- Blueprint::UP c2 = ap(MyLeafSpec(10).create());
- Blueprint::UP c3 = ap(MyLeafSpec(40).create());
- Blueprint::UP c4 = ap(MyLeafSpec(30).create());
- children.push_back(c1.get());
- children.push_back(c2.get());
- children.push_back(c3.get());
- children.push_back(c4.get());
- b.sort(children);
- EXPECT_EQUAL(c1.get(), children[0]);
- EXPECT_EQUAL(c2.get(), children[1]);
- EXPECT_EQUAL(c3.get(), children[2]);
- EXPECT_EQUAL(c4.get(), children[3]);
+ check_sort_order(b,
+ {MyLeafSpec(20).create(),
+ MyLeafSpec(10).create(),
+ MyLeafSpec(40).create(),
+ MyLeafSpec(30).create()},
+ {0, 1, 2, 3});
}
{
EXPECT_EQUAL(true, b.inheritStrict(0));
@@ -1080,20 +1036,12 @@ TEST("test WeakAnd Blueprint") {
EXPECT_EQUAL(0u, a.exposeFields().size());
}
{
- std::vector<Blueprint *> children;
- Blueprint::UP c1 = ap(MyLeafSpec(10).create());
- Blueprint::UP c2 = ap(MyLeafSpec(20).create());
- Blueprint::UP c3 = ap(MyLeafSpec(40).create());
- Blueprint::UP c4 = ap(MyLeafSpec(30).create());
- children.push_back(c1.get());
- children.push_back(c2.get());
- children.push_back(c3.get());
- children.push_back(c4.get());
- b.sort(children);
- EXPECT_EQUAL(c1.get(), children[0]);
- EXPECT_EQUAL(c2.get(), children[1]);
- EXPECT_EQUAL(c3.get(), children[2]);
- EXPECT_EQUAL(c4.get(), children[3]);
+ check_sort_order(b,
+ {MyLeafSpec(20).create(),
+ MyLeafSpec(10).create(),
+ MyLeafSpec(40).create(),
+ MyLeafSpec(30).create()},
+ {0, 1, 2, 3});
}
{
EXPECT_EQUAL(true, b.inheritStrict(0));
diff --git a/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp b/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp
index fe9f6424b9c..ecb7ee3b48b 100644
--- a/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp
+++ b/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp
@@ -2,18 +2,28 @@
#include <vespa/searchlib/queryeval/simpleresult.h>
#include <vespa/searchlib/queryeval/blueprint.h>
+#include <vespa/searchlib/queryeval/intermediate_blueprints.h>
#include <vespa/searchlib/queryeval/leaf_blueprints.h>
+#include <vespa/searchlib/queryeval/isourceselector.h>
+#include <vespa/searchlib/queryeval/simple_phrase_blueprint.h>
+#include <vespa/searchlib/queryeval/equiv_blueprint.h>
+#include <vespa/searchlib/queryeval/weighted_set_term_blueprint.h>
+#include <vespa/searchlib/queryeval/dot_product_blueprint.h>
+#include <vespa/searchlib/queryeval/same_element_blueprint.h>
+#include <vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h>
+#include <vespa/searchlib/fef/matchdatalayout.h>
#include <vespa/vespalib/util/trinary.h>
#include <vespa/vespalib/util/require.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <functional>
-using search::queryeval::AlwaysTrueBlueprint;
-using search::queryeval::Blueprint;
-using search::queryeval::EmptyBlueprint;
-using search::queryeval::SimpleBlueprint;
-using search::queryeval::SimpleResult;
-using search::queryeval::SearchIterator;
+namespace search::fef { class TermFieldMatchDataArray; }
+namespace search::fef { class MatchData; }
+
+using namespace search::queryeval;
+using search::fef::MatchData;
+using search::fef::MatchDataLayout;
+using search::fef::TermFieldMatchDataArray;
using vespalib::Trinary;
using Constraint = Blueprint::FilterConstraint;
@@ -27,37 +37,48 @@ concept FilterFactory = requires(const T &a, bool strict, Constraint upper_or_lo
{ a.createFilterSearch(strict, upper_or_lower) } -> std::same_as<std::unique_ptr<SearchIterator>>;
};
-using factory_fun = std::function<std::unique_ptr<SearchIterator>(const std::vector<Blueprint*> &, bool, Constraint)>;
+template <typename T>
+concept FilterFactoryBuilder = requires(T a, std::unique_ptr<Blueprint> bp) {
+ { a.add(std::move(bp)) } -> std::same_as<T&>;
+};
-// Combine children blueprints using a shared filter creation
-// algorithm. Satisfies the FilterFactory concept.
-struct Combine {
- factory_fun fun;
- std::vector<Blueprint*> list;
- Combine(factory_fun fun_in) : fun(fun_in), list() {}
- Combine &&add(std::unique_ptr<Blueprint> child) && {
- list.push_back(child.release());
- return std::move(*this);
- }
- auto createFilterSearch(bool strict, Constraint upper_or_lower) const {
- return fun(list, strict, upper_or_lower);
+template <typename T>
+concept ChildCollector = requires(T a, std::unique_ptr<Blueprint> bp) {
+ a.addChild(std::move(bp));
+};
+
+// inherit Blueprint to capture the default filter factory
+struct DefaultBlueprint : Blueprint {
+ void optimize(Blueprint* &) override { abort(); }
+ const State &getState() const override { abort(); }
+ void fetchPostings(const ExecuteInfo &) override { abort(); }
+ void freeze() override { abort(); }
+ SearchIteratorUP createSearch(MatchData &, bool) const override { abort(); }
+};
+
+// add the use of a field to a leaf blueprint (SimplePhraseBlueprint asserts on this)
+struct FakeFieldProxy : SimpleLeafBlueprint {
+ std::unique_ptr<Blueprint> child;
+ FakeFieldProxy(const FieldSpec &field, std::unique_ptr<Blueprint> child_in)
+ : SimpleLeafBlueprint(field), child(std::move(child_in))
+ {
+ setParent(child->getParent());
+ child->setParent(this);
}
- ~Combine() {
- for (auto *ptr: list) {
- delete ptr;
- }
+ SearchIteratorUP createLeafSearch(const TermFieldMatchDataArray &, bool) const override { abort(); }
+ SearchIteratorUP createFilterSearch(bool strict, Constraint upper_or_lower) const override {
+ return child->createFilterSearch(strict, upper_or_lower);
}
};
-// create a leaf blueprint that matches no documents
-std::unique_ptr<Blueprint> empty() {
- return std::make_unique<EmptyBlueprint>();
-}
-
-// create a leaf blueprint that matches all documents
-std::unique_ptr<Blueprint> full() {
- return std::make_unique<AlwaysTrueBlueprint>();
-}
+// need one of these to be able to create a SourceBlender
+struct NullSelector : ISourceSelector {
+ NullSelector() : ISourceSelector(7) {}
+ void setSource(uint32_t, Source) override { abort(); }
+ uint32_t getDocIdLimit() const override { abort(); }
+ void compactLidSpace(uint32_t) override { abort(); }
+ std::unique_ptr<sourceselector::Iterator> createIterator() const override { abort(); }
+};
// make a simple result containing the given documents
SimpleResult make_result(const std::vector<uint32_t> &docs) {
@@ -82,25 +103,205 @@ SimpleResult make_empty_result() {
return SimpleResult();
}
+// create a leaf blueprint that matches no documents
+std::unique_ptr<Blueprint> empty() {
+ return std::make_unique<EmptyBlueprint>();
+}
+
+// create a leaf blueprint that matches all documents
+std::unique_ptr<Blueprint> full() {
+ return std::make_unique<AlwaysTrueBlueprint>();
+}
+
// create a leaf blueprint with the specified hits
-std::unique_ptr<Blueprint> leaf(const std::vector<uint32_t> &docs) {
+std::unique_ptr<Blueprint> hits(const std::vector<uint32_t> &docs) {
return std::make_unique<SimpleBlueprint>(make_result(docs));
}
+// Describes blueprint children with a list of simple factories that
+// can later be used to create them.
+struct Children {
+ using Factory = std::function<Blueprint::UP()>;
+ std::vector<Factory> list;
+ Children() : list() {}
+ size_t size() const { return list.size(); }
+ Children &hits(const std::vector<uint32_t> &docs) {
+ list.push_back([docs](){ return ::hits(docs); });
+ return *this;
+ }
+ Children &full() {
+ list.push_back([](){ return ::full(); });
+ return *this;
+ }
+ Children &empty() {
+ list.push_back([](){ return ::empty(); });
+ return *this;
+ }
+ template <FilterFactoryBuilder Builder>
+ Builder &apply(Builder &builder) const {
+ for (const Factory &make_child: list) {
+ builder.add(make_child());
+ }
+ return builder;
+ }
+};
+
+// Combine children blueprints using a shared filter creation
+// algorithm. Satisfies the FilterFactory concept.
+struct Combine {
+ using factory_fun = std::function<std::unique_ptr<SearchIterator>(const Blueprint::Children &, bool, Constraint)>;
+ factory_fun fun;
+ Blueprint::Children list;
+ Combine(factory_fun fun_in) noexcept : fun(fun_in), list() {}
+ Combine &add(std::unique_ptr<Blueprint> child) {
+ list.push_back(std::move(child));
+ return *this;
+ }
+ Combine &add(const Children &children) {
+ return children.apply(*this);
+ }
+ auto createFilterSearch(bool strict, Constraint upper_or_lower) const {
+ return fun(list, strict, upper_or_lower);
+ }
+ ~Combine();
+};
+Combine::~Combine() = default;
+
+// enable Make-ing source blender
+struct SourceBlenderAdapter {
+ NullSelector selector;
+ SourceBlenderBlueprint blueprint;
+ SourceBlenderAdapter() : selector(), blueprint(selector) {}
+ void addChild(std::unique_ptr<Blueprint> child) {
+ blueprint.addChild(std::move(child));
+ }
+ auto createFilterSearch(bool strict, Constraint upper_or_lower) const {
+ return blueprint.createFilterSearch(strict, upper_or_lower);
+ }
+};
+
+// enable Make-ing simple phrase
+struct SimplePhraseAdapter {
+ FieldSpec field;
+ SimplePhraseBlueprint blueprint;
+ SimplePhraseAdapter() : field("foo", 3, 7), blueprint(field, false) {}
+ void addChild(std::unique_ptr<Blueprint> child) {
+ auto child_field = blueprint.getNextChildField(field);
+ auto term = std::make_unique<FakeFieldProxy>(child_field, std::move(child));
+ blueprint.addTerm(std::move(term));
+ }
+ auto createFilterSearch(bool strict, Constraint upper_or_lower) const {
+ return blueprint.createFilterSearch(strict, upper_or_lower);
+ }
+};
+
+//enable Make-ing equiv
+struct EquivAdapter {
+ FieldSpecBaseList fields;
+ EquivBlueprint blueprint;
+ EquivAdapter() : fields(), blueprint(fields, MatchDataLayout()) {}
+ void addChild(std::unique_ptr<Blueprint> child) {
+ blueprint.addTerm(std::move(child), 1.0);
+ }
+ auto createFilterSearch(bool strict, Constraint upper_or_lower) const {
+ return blueprint.createFilterSearch(strict, upper_or_lower);
+ }
+};
+
+// enable Make-ing weighted set
+struct WeightedSetTermAdapter {
+ FieldSpec field;
+ WeightedSetTermBlueprint blueprint;
+ WeightedSetTermAdapter() : field("foo", 3, 7), blueprint(field) {}
+ void addChild(std::unique_ptr<Blueprint> child) {
+ blueprint.addTerm(std::move(child), 100);
+ }
+ auto createFilterSearch(bool strict, Constraint upper_or_lower) const {
+ return blueprint.createFilterSearch(strict, upper_or_lower);
+ }
+};
+
+// enable Make-ing dot product
+struct DotProductAdapter {
+ FieldSpec field;
+ DotProductBlueprint blueprint;
+ DotProductAdapter() : field("foo", 3, 7), blueprint(field) {}
+ void addChild(std::unique_ptr<Blueprint> child) {
+ auto child_field = blueprint.getNextChildField(field);
+ auto term = std::make_unique<FakeFieldProxy>(child_field, std::move(child));
+ blueprint.addTerm(std::move(term), 100);
+ }
+ auto createFilterSearch(bool strict, Constraint upper_or_lower) const {
+ return blueprint.createFilterSearch(strict, upper_or_lower);
+ }
+};
+
+// enable Make-ing parallel weak and
+struct ParallelWeakAndAdapter {
+ FieldSpec field;
+ ParallelWeakAndBlueprint blueprint;
+ ParallelWeakAndAdapter() : field("foo", 3, 7), blueprint(field, 100, 0.0, 1.0) {}
+ void addChild(std::unique_ptr<Blueprint> child) {
+ auto child_field = blueprint.getNextChildField(field);
+ auto term = std::make_unique<FakeFieldProxy>(child_field, std::move(child));
+ blueprint.addTerm(std::move(term), 100);
+ }
+ auto createFilterSearch(bool strict, Constraint upper_or_lower) const {
+ return blueprint.createFilterSearch(strict, upper_or_lower);
+ }
+};
+
+// enable Make-ing same element
+struct SameElementAdapter {
+ SameElementBlueprint blueprint;
+ SameElementAdapter() : blueprint("foo", false) {}
+ void addChild(std::unique_ptr<Blueprint> child) {
+ auto child_field = blueprint.getNextChildField("foo", 3);
+ auto term = std::make_unique<FakeFieldProxy>(child_field, std::move(child));
+ blueprint.addTerm(std::move(term));
+ }
+ auto createFilterSearch(bool strict, Constraint upper_or_lower) const {
+ return blueprint.createFilterSearch(strict, upper_or_lower);
+ }
+};
+
+// Make a specific intermediate-ish blueprint that you can add
+// children to. Satisfies the FilterFactory concept.
+template <FilterFactory T>
+requires ChildCollector<T>
+struct Make {
+ T blueprint;
+ template <typename ... Args>
+ Make(Args && ... args) : blueprint(std::forward<Args>(args)...) {}
+ Make &add(std::unique_ptr<Blueprint> child) {
+ blueprint.addChild(std::move(child));
+ return *this;
+ }
+ Make &add(const Children &children) {
+ return children.apply(*this);
+ }
+ auto createFilterSearch(bool strict, Constraint upper_or_lower) const {
+ return blueprint.createFilterSearch(strict, upper_or_lower);
+ }
+};
+
// what kind of results are we expecting from a filter search?
struct Expect {
Trinary matches_any;
- SimpleResult hits;
- Expect(const std::vector<uint32_t> &hits_in)
- : matches_any(Trinary::Undefined), hits(make_result(hits_in)) {}
- Expect(Trinary matches_any_in) : matches_any(matches_any_in), hits() {
+ SimpleResult docs;
+ Expect(const std::vector<uint32_t> &docs_in)
+ : matches_any(Trinary::Undefined), docs(make_result(docs_in)) {}
+ Expect(Trinary matches_any_in) : matches_any(matches_any_in), docs() {
REQUIRE(matches_any != Trinary::Undefined);
if (matches_any == Trinary::True) {
- hits = make_full_result();
+ docs = make_full_result();
} else {
- hits = make_empty_result();
+ docs = make_empty_result();
}
}
+ static Expect empty() { return Expect(Trinary::False); }
+ static Expect full() { return Expect(Trinary::True); }
+ static Expect hits(const std::vector<uint32_t> &docs) { return Expect(docs); }
};
template <FilterFactory Blueprint>
@@ -116,7 +317,7 @@ void verify(const Blueprint &blueprint, const Expect &upper, const Expect &lower
} else {
actual.search(*filter, docid_limit);
}
- EXPECT_EQ(actual, expect.hits);
+ EXPECT_EQ(actual, expect.docs);
}
}
}
@@ -127,31 +328,73 @@ void verify(const Blueprint &blueprint, const Expect &upper_and_lower) {
}
TEST(FilterSearchTest, empty_leaf) {
- verify(*empty(), Expect(Trinary::False));
+ verify(*empty(), Expect::empty());
}
TEST(FilterSearchTest, full_leaf) {
- verify(*full(), Expect(Trinary::True));
+ verify(*full(), Expect::full());
}
TEST(FilterSearchTest, custom_leaf) {
- verify(*leaf({5,10,20}), Expect({5,10,20}));
+ verify(*hits({5,10,20}), Expect::hits({5,10,20}));
+}
+
+TEST(FilterSearchTest, default_blueprint) {
+ verify(DefaultBlueprint(), Expect::full(), Expect::empty());
}
TEST(FilterSearchTest, simple_or) {
- verify(Combine(Blueprint::create_or_filter)
- .add(leaf({5, 10}))
- .add(leaf({7}))
- .add(leaf({3, 11})),
- Expect({3, 5, 7, 10, 11}));
+ auto child_list = Children()
+ .hits({5, 10})
+ .hits({7})
+ .hits({3, 11});
+ auto expected = Expect::hits({3, 5, 7, 10, 11});
+ verify(Combine(Blueprint::create_or_filter).add(child_list), expected);
+ verify(Make<OrBlueprint>().add(child_list), expected);
+ verify(Make<EquivAdapter>().add(child_list), expected);
+ verify(Make<WeightedSetTermAdapter>().add(child_list), expected);
+ verify(Make<DotProductAdapter>().add(child_list), expected);
+ verify(Combine(Blueprint::create_atmost_or_filter).add(child_list), expected, Expect::empty());
+ verify(Make<WeakAndBlueprint>(100).add(child_list), expected, Expect::empty());
+ verify(Make<SourceBlenderAdapter>().add(child_list), expected, Expect::empty());
+ verify(Make<ParallelWeakAndAdapter>().add(child_list), expected, Expect::empty());
}
TEST(FilterSearchTest, simple_and) {
- verify(Combine(Blueprint::create_and_filter)
- .add(leaf({1, 2, 3, 4, 5, 6}))
- .add(leaf({2, 4, 6, 7}))
- .add(leaf({1, 4, 6, 7, 10})),
- Expect({4, 6}));
+ auto child_list = Children()
+ .hits({1, 2, 3, 4, 5, 6})
+ .hits({2, 4, 6, 7})
+ .hits({1, 4, 6, 7, 10});
+ auto expected = Expect::hits({4, 6});
+ verify(Combine(Blueprint::create_and_filter).add(child_list), expected);
+ verify(Make<AndBlueprint>().add(child_list), expected);
+ verify(Combine(Blueprint::create_atmost_and_filter).add(child_list), expected, Expect::empty());
+ verify(Make<NearBlueprint>(3).add(child_list), expected, Expect::empty());
+ verify(Make<ONearBlueprint>(3).add(child_list), expected, Expect::empty());
+ verify(Make<SimplePhraseAdapter>().add(child_list), expected, Expect::empty());
+ verify(Make<SameElementAdapter>().add(child_list), expected, Expect::empty());
+}
+
+TEST(FilterSearchTest, simple_andnot) {
+ auto child_list = Children()
+ .hits({1, 2, 3, 4, 5, 6})
+ .hits({2, 4, 6})
+ .hits({4, 6, 7});
+ auto expected = Expect::hits({1, 3, 5});
+ verify(Combine(Blueprint::create_andnot_filter).add(child_list), expected);
+ verify(Make<AndNotBlueprint>().add(child_list), expected);
+}
+
+TEST(FilterSearchTest, rank_filter) {
+ auto child_list1 = Children().hits({1,2,3}).empty().full();
+ auto child_list2 = Children().empty().hits({1,2,3}).full();
+ auto child_list3 = Children().full().hits({1,2,3}).empty();
+ verify(Combine(Blueprint::create_first_child_filter).add(child_list1), Expect::hits({1,2,3}));
+ verify(Combine(Blueprint::create_first_child_filter).add(child_list2), Expect::empty());
+ verify(Combine(Blueprint::create_first_child_filter).add(child_list3), Expect::full());
+ verify(Make<RankBlueprint>().add(child_list1), Expect::hits({1,2,3}));
+ verify(Make<RankBlueprint>().add(child_list2), Expect::empty());
+ verify(Make<RankBlueprint>().add(child_list3), Expect::full());
}
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp b/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp
index 9fccad1d2d4..3a50601abbd 100644
--- a/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp
+++ b/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp
@@ -118,21 +118,21 @@ public:
CompactionStrategy compaction_strategy;
return index->update_stat(compaction_strategy);
}
- void expect_entry_point(uint32_t exp_docid, uint32_t exp_level) {
- EXPECT_EQ(exp_docid, index->get_entry_docid());
+ void expect_entry_point(uint32_t exp_nodeid, uint32_t exp_level) {
+ EXPECT_EQ(exp_nodeid, index->get_entry_nodeid());
EXPECT_EQ(exp_level, index->get_entry_level());
}
- void expect_level_0(uint32_t docid, const HnswNode::LinkArray& exp_links) {
- auto node = index->get_node(docid);
+ void expect_level_0(uint32_t nodeid, const HnswNode::LinkArray& exp_links) {
+ auto node = index->get_node(nodeid);
ASSERT_EQ(1, node.size());
EXPECT_EQ(exp_links, node.level(0));
}
- void expect_empty_level_0(uint32_t docid) {
- auto node = index->get_node(docid);
+ void expect_empty_level_0(uint32_t nodeid) {
+ auto node = index->get_node(nodeid);
EXPECT_TRUE(node.empty());
}
- void expect_levels(uint32_t docid, const HnswNode::LevelArray& exp_levels) {
- auto act_node = index->get_node(docid);
+ void expect_levels(uint32_t nodeid, const HnswNode::LevelArray& exp_levels) {
+ auto act_node = index->get_node(nodeid);
ASSERT_EQ(exp_levels.size(), act_node.size());
EXPECT_EQ(exp_levels, act_node.levels());
}
@@ -144,7 +144,7 @@ public:
size_t idx = 0;
for (const auto & hit : rv) {
if (idx < exp_hits.size()) {
- EXPECT_EQ(hit.docid, exp_hits[idx++]);
+ EXPECT_EQ(index->get_docid(hit.nodeid), exp_hits[idx++]);
}
}
if (exp_hits.size() == k) {
@@ -169,7 +169,7 @@ public:
? index->find_top_k_with_filter(k, qv, *global_filter, k, thr)
: index->find_top_k(k, qv, k, thr);
EXPECT_EQ(got_by_docid.size(), 1);
- EXPECT_EQ(got_by_docid[0].docid, rv[0].docid);
+ EXPECT_EQ(got_by_docid[0].docid, index->get_docid(rv[0].nodeid));
for (const auto & hit : got_by_docid) {
LOG(debug, "from docid=%u found docid=%u dist=%g (threshold %g)\n",
docid, hit.docid, hit.distance, thr);
diff --git a/searchlib/src/tests/tensor/hnsw_saver/hnsw_save_load_test.cpp b/searchlib/src/tests/tensor/hnsw_saver/hnsw_save_load_test.cpp
index add7801e03a..ea22aaabaff 100644
--- a/searchlib/src/tests/tensor/hnsw_saver/hnsw_save_load_test.cpp
+++ b/searchlib/src/tests/tensor/hnsw_saver/hnsw_save_load_test.cpp
@@ -52,12 +52,12 @@ using V = std::vector<uint32_t>;
void populate(HnswGraph &graph) {
// no 0
- graph.make_node_for_document(1, 1);
- auto er = graph.make_node_for_document(2, 2);
+ graph.make_node(1, 1);
+ auto er = graph.make_node(2, 2);
// no 3
- graph.make_node_for_document(4, 2);
- graph.make_node_for_document(5, 0);
- graph.make_node_for_document(6, 1);
+ graph.make_node(4, 2);
+ graph.make_node(5, 0);
+ graph.make_node(6, 1);
graph.set_link_array(1, 0, V{2, 4, 6});
graph.set_link_array(2, 0, V{1, 4, 6});
@@ -69,9 +69,9 @@ void populate(HnswGraph &graph) {
}
void modify(HnswGraph &graph) {
- graph.remove_node_for_document(2);
- graph.remove_node_for_document(6);
- graph.make_node_for_document(7, 2);
+ graph.remove_node(2);
+ graph.remove_node(6);
+ graph.make_node(7, 2);
graph.set_link_array(1, 0, V{7, 4});
graph.set_link_array(4, 0, V{7, 2});
@@ -88,24 +88,24 @@ public:
HnswGraph original;
HnswGraph copy;
- void expect_empty_d(uint32_t docid) const {
- EXPECT_FALSE(copy.acquire_node_ref(docid).valid());
+ void expect_empty_d(uint32_t nodeid) const {
+ EXPECT_FALSE(copy.acquire_node_ref(nodeid).valid());
}
- void expect_level_0(uint32_t docid, const V& exp_links) const {
- auto levels = copy.acquire_level_array(docid);
+ void expect_level_0(uint32_t nodeid, const V& exp_links) const {
+ auto levels = copy.acquire_level_array(nodeid);
EXPECT_GE(levels.size(), 1);
- auto links = copy.acquire_link_array(docid, 0);
+ auto links = copy.acquire_link_array(nodeid, 0);
EXPECT_EQ(exp_links.size(), links.size());
for (size_t i = 0; i < exp_links.size() && i < links.size(); ++i) {
EXPECT_EQ(exp_links[i], links[i]);
}
}
- void expect_level_1(uint32_t docid, const V& exp_links) const {
- auto levels = copy.acquire_level_array(docid);
+ void expect_level_1(uint32_t nodeid, const V& exp_links) const {
+ auto levels = copy.acquire_level_array(nodeid);
EXPECT_EQ(2, levels.size());
- auto links = copy.acquire_link_array(docid, 1);
+ auto links = copy.acquire_link_array(nodeid, 1);
EXPECT_EQ(exp_links.size(), links.size());
for (size_t i = 0; i < exp_links.size() && i < links.size(); ++i) {
EXPECT_EQ(exp_links[i], links[i]);
@@ -126,7 +126,7 @@ public:
void expect_copy_as_populated() const {
EXPECT_EQ(copy.size(), 7);
auto entry = copy.get_entry_node();
- EXPECT_EQ(entry.docid, 2);
+ EXPECT_EQ(entry.nodeid, 2);
EXPECT_EQ(entry.level, 1);
expect_empty_d(0);
diff --git a/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
index ff83dd92f3b..29a9fd96118 100644
--- a/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
@@ -8,6 +8,7 @@
#include "field_spec.hpp"
#include "andsearch.h"
#include "orsearch.h"
+#include "andnotsearch.h"
#include "matching_elements_search.h"
#include <vespa/searchlib/fef/termfieldmatchdataarray.h>
#include <vespa/vespalib/objects/visit.hpp>
@@ -159,9 +160,19 @@ Blueprint::create_matching_elements_search(const MatchingElementsFields &fields)
namespace {
+Blueprint::FilterConstraint invert(Blueprint::FilterConstraint constraint) {
+ if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
+ return Blueprint::FilterConstraint::LOWER_BOUND;
+ }
+ if (constraint == Blueprint::FilterConstraint::LOWER_BOUND) {
+ return Blueprint::FilterConstraint::UPPER_BOUND;
+ }
+ abort();
+}
+
template <typename Op>
std::unique_ptr<SearchIterator>
-create_op_filter(const std::vector<Blueprint *>& children, bool strict, Blueprint::FilterConstraint constraint)
+create_op_filter(const Blueprint::Children &children, bool strict, Blueprint::FilterConstraint constraint)
{
MultiSearch::Children sub_searches;
sub_searches.reserve(children.size());
@@ -178,17 +189,58 @@ create_op_filter(const std::vector<Blueprint *>& children, bool strict, Blueprin
}
std::unique_ptr<SearchIterator>
-Blueprint::create_and_filter(const std::vector<Blueprint *>& children, bool strict, Blueprint::FilterConstraint constraint)
+Blueprint::create_and_filter(const Children &children, bool strict, Blueprint::FilterConstraint constraint)
{
return create_op_filter<AndSearch>(children, strict, constraint);
}
std::unique_ptr<SearchIterator>
-Blueprint::create_or_filter(const std::vector<Blueprint *>& children, bool strict, Blueprint::FilterConstraint constraint)
+Blueprint::create_or_filter(const Children &children, bool strict, Blueprint::FilterConstraint constraint)
{
return create_op_filter<OrSearch>(children, strict, constraint);
}
+std::unique_ptr<SearchIterator>
+Blueprint::create_atmost_and_filter(const Children &children, bool strict, Blueprint::FilterConstraint constraint)
+{
+ if (constraint == FilterConstraint::UPPER_BOUND) {
+ return create_and_filter(children, strict, constraint);
+ } else {
+ return std::make_unique<EmptySearch>();
+ }
+}
+
+std::unique_ptr<SearchIterator>
+Blueprint::create_atmost_or_filter(const Children &children, bool strict, Blueprint::FilterConstraint constraint)
+{
+ if (constraint == FilterConstraint::UPPER_BOUND) {
+ return create_or_filter(children, strict, constraint);
+ } else {
+ return std::make_unique<EmptySearch>();
+ }
+}
+
+std::unique_ptr<SearchIterator>
+Blueprint::create_andnot_filter(const Children &children, bool strict, Blueprint::FilterConstraint constraint)
+{
+ MultiSearch::Children sub_searches;
+ sub_searches.reserve(children.size());
+ for (size_t i = 0; i < children.size(); ++i) {
+ auto search = (i == 0)
+ ? children[i]->createFilterSearch(strict, constraint)
+ : children[i]->createFilterSearch(false, invert(constraint));
+ sub_searches.push_back(std::move(search));
+ }
+ return AndNotSearch::create(std::move(sub_searches), strict);
+}
+
+std::unique_ptr<SearchIterator>
+Blueprint::create_first_child_filter(const Children &children, bool strict, Blueprint::FilterConstraint constraint)
+{
+ assert(children.size() > 0);
+ return children[0]->createFilterSearch(strict, constraint);
+}
+
vespalib::string
Blueprint::asString() const
{
@@ -263,19 +315,13 @@ StateCache::notifyChange() {
//-----------------------------------------------------------------------------
-IntermediateBlueprint::~IntermediateBlueprint()
-{
- while (!_children.empty()) {
- delete _children.back();
- _children.pop_back();
- }
-}
+IntermediateBlueprint::~IntermediateBlueprint() = default;
void
IntermediateBlueprint::setDocIdLimit(uint32_t limit)
{
Blueprint::setDocIdLimit(limit);
- for (Blueprint * child : _children) {
+ for (Blueprint::UP &child : _children) {
child->setDocIdLimit(limit);
}
}
@@ -285,7 +331,7 @@ IntermediateBlueprint::calculateEstimate() const
{
std::vector<HitEstimate> estimates;
estimates.reserve(_children.size());
- for (const Blueprint * child : _children) {
+ for (const Blueprint::UP &child : _children) {
estimates.push_back(child->getState().estimate());
}
return combine(estimates);
@@ -295,7 +341,7 @@ uint32_t
IntermediateBlueprint::calculate_cost_tier() const
{
uint32_t cost_tier = State::COST_TIER_MAX;
- for (const Blueprint * child : _children) {
+ for (const Blueprint::UP &child : _children) {
cost_tier = std::min(cost_tier, child->getState().cost_tier());
}
return cost_tier;
@@ -305,7 +351,7 @@ uint32_t
IntermediateBlueprint::calculate_tree_size() const
{
uint32_t nodes = 1;
- for (const Blueprint * child : _children) {
+ for (const Blueprint::UP &child : _children) {
nodes += child->getState().tree_size();
}
return nodes;
@@ -317,7 +363,7 @@ IntermediateBlueprint::infer_allow_termwise_eval() const
if (!supports_termwise_children()) {
return false;
}
- for (const Blueprint * child : _children) {
+ for (const Blueprint::UP &child : _children) {
if (!child->getState().allow_termwise_eval()) {
return false;
}
@@ -328,7 +374,7 @@ IntermediateBlueprint::infer_allow_termwise_eval() const
bool
IntermediateBlueprint::infer_want_global_filter() const
{
- for (const Blueprint * child : _children) {
+ for (const Blueprint::UP &child : _children) {
if (child->getState().want_global_filter()) {
return true;
}
@@ -371,7 +417,7 @@ IntermediateBlueprint::mixChildrenFields() const
Map fieldMap;
FieldSpecBaseList fieldList;
- for (const Blueprint * child : _children) {
+ for (const Blueprint::UP &child : _children) {
const State &childState = child->getState();
if (!childState.isTermLike()) {
return fieldList; // empty: non-term-like child
@@ -431,8 +477,10 @@ IntermediateBlueprint::optimize(Blueprint* &self)
{
assert(self == this);
if (should_optimize_children()) {
- for (auto & child : _children) {
- child->optimize(child);
+ for (auto &child : _children) {
+ auto *child_ptr = child.release();
+ child_ptr->optimize(child_ptr);
+ child.reset(child_ptr);
}
}
optimize_self();
@@ -467,8 +515,8 @@ IntermediateBlueprint::IntermediateBlueprint() = default;
IntermediateBlueprint &
IntermediateBlueprint::addChild(Blueprint::UP child)
{
- _children.push_back(child.get());
- child.release()->setParent(this);
+ child->setParent(this);
+ _children.push_back(std::move(child));
notifyChange();
return *this;
}
@@ -477,9 +525,9 @@ Blueprint::UP
IntermediateBlueprint::removeChild(size_t n)
{
assert(n < _children.size());
- Blueprint::UP ret(_children[n]);
+ Blueprint::UP ret = std::move(_children[n]);
_children.erase(_children.begin() + n);
- ret->setParent(0);
+ ret->setParent(nullptr);
notifyChange();
return ret;
}
@@ -488,8 +536,8 @@ IntermediateBlueprint &
IntermediateBlueprint::insertChild(size_t n, Blueprint::UP child)
{
assert(n <= _children.size());
- _children.insert(_children.begin() + n, child.get());
- child.release()->setParent(this);
+ child->setParent(this);
+ _children.insert(_children.begin() + n, std::move(child));
notifyChange();
return *this;
}
@@ -515,7 +563,7 @@ IntermediateBlueprint::fetchPostings(const ExecuteInfo &execInfo)
void
IntermediateBlueprint::freeze()
{
- for (Blueprint * child : _children) {
+ for (Blueprint::UP &child: _children) {
child->freeze();
}
freeze_self();
diff --git a/searchlib/src/vespa/searchlib/queryeval/blueprint.h b/searchlib/src/vespa/searchlib/queryeval/blueprint.h
index 6dbd3cb9a5c..199cf45792d 100644
--- a/searchlib/src/vespa/searchlib/queryeval/blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/blueprint.h
@@ -40,8 +40,9 @@ class MatchingElementsSearch;
class Blueprint
{
public:
- typedef std::unique_ptr<Blueprint> UP;
- typedef std::unique_ptr<SearchIterator> SearchIteratorUP;
+ using UP = std::unique_ptr<Blueprint>;
+ using Children = std::vector<Blueprint::UP>;
+ using SearchIteratorUP = std::unique_ptr<SearchIterator>;
struct HitEstimate {
uint32_t estHits;
@@ -127,7 +128,7 @@ public:
// utility to get the greater estimate to sort first, higher tiers last
struct TieredGreaterEstimate {
- bool operator () (Blueprint * const &a, Blueprint * const &b) const {
+ bool operator () (const auto &a, const auto &b) const {
const auto &lhs = a->getState();
const auto &rhs = b->getState();
if (lhs.cost_tier() != rhs.cost_tier()) {
@@ -139,7 +140,7 @@ public:
// utility to get the lesser estimate to sort first, higher tiers last
struct TieredLessEstimate {
- bool operator () (Blueprint * const &a, const Blueprint * const &b) const {
+ bool operator () (const auto &a, const auto &b) const {
const auto &lhs = a->getState();
const auto &rhs = b->getState();
if (lhs.cost_tier() != rhs.cost_tier()) {
@@ -225,8 +226,12 @@ public:
virtual SearchIteratorUP createSearch(fef::MatchData &md, bool strict) const = 0;
virtual SearchIteratorUP createFilterSearch(bool strict, FilterConstraint constraint) const;
- static std::unique_ptr<SearchIterator> create_and_filter(const std::vector<Blueprint *>& children, bool strict, FilterConstraint constraint);
- static std::unique_ptr<SearchIterator> create_or_filter(const std::vector<Blueprint *>& children, bool strict, FilterConstraint constraint);
+ static SearchIteratorUP create_and_filter(const Children &children, bool strict, FilterConstraint constraint);
+ static SearchIteratorUP create_or_filter(const Children &children, bool strict, FilterConstraint constraint);
+ static SearchIteratorUP create_atmost_and_filter(const Children &children, bool strict, FilterConstraint constraint);
+ static SearchIteratorUP create_atmost_or_filter(const Children &children, bool strict, FilterConstraint constraint);
+ static SearchIteratorUP create_andnot_filter(const Children &children, bool strict, FilterConstraint constraint);
+ static SearchIteratorUP create_first_child_filter(const Children &children, bool strict, FilterConstraint constraint);
// for debug dumping
vespalib::string asString() const;
@@ -278,8 +283,6 @@ public:
class IntermediateBlueprint : public blueprint::StateCache
{
-public:
- typedef std::vector<Blueprint*> Children;
private:
Children _children;
HitEstimate calculateEstimate() const;
@@ -325,7 +328,7 @@ public:
virtual HitEstimate combine(const std::vector<HitEstimate> &data) const = 0;
virtual FieldSpecBaseList exposeFields() const = 0;
- virtual void sort(std::vector<Blueprint*> &children) const = 0;
+ virtual void sort(Children &children) const = 0;
virtual bool inheritStrict(size_t i) const = 0;
virtual SearchIteratorUP
createIntermediateSearch(MultiSearch::Children subSearches,
diff --git a/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.cpp
index 1e2b8109778..61b717b1104 100644
--- a/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.cpp
@@ -16,13 +16,7 @@ DotProductBlueprint::DotProductBlueprint(const FieldSpec &field)
{
}
-DotProductBlueprint::~DotProductBlueprint()
-{
- while (!_terms.empty()) {
- delete _terms.back();
- _terms.pop_back();
- }
-}
+DotProductBlueprint::~DotProductBlueprint() = default;
FieldSpec
DotProductBlueprint::getNextChildField(const FieldSpec &outer)
@@ -43,8 +37,7 @@ DotProductBlueprint::addTerm(Blueprint::UP term, int32_t weight)
setEstimate(_estimate);
}
_weights.push_back(weight);
- _terms.push_back(term.get());
- term.release();
+ _terms.push_back(std::move(term));
}
SearchIterator::UP
diff --git a/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.h b/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.h
index d1992fc67c5..4ba59ba755f 100644
--- a/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/dot_product_blueprint.h
@@ -11,10 +11,10 @@ namespace search::queryeval {
class DotProductBlueprint : public ComplexLeafBlueprint
{
- HitEstimate _estimate;
- fef::MatchDataLayout _layout;
- std::vector<int32_t> _weights;
- std::vector<Blueprint*> _terms;
+ HitEstimate _estimate;
+ fef::MatchDataLayout _layout;
+ std::vector<int32_t> _weights;
+ std::vector<Blueprint::UP> _terms;
public:
explicit DotProductBlueprint(const FieldSpec &field);
diff --git a/searchlib/src/vespa/searchlib/queryeval/equiv_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/equiv_blueprint.cpp
index d93134447d0..c2f4c53224d 100644
--- a/searchlib/src/vespa/searchlib/queryeval/equiv_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/equiv_blueprint.cpp
@@ -79,13 +79,7 @@ EquivBlueprint::createLeafSearch(const fef::TermFieldMatchDataArray &outputs, bo
SearchIterator::UP
EquivBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- MultiSearch::Children children;
- children.reserve(_terms.size());
- for (size_t i = 0; i < _terms.size(); ++i) {
- children.push_back(_terms[i]->createFilterSearch(strict, constraint));
- }
- UnpackInfo unpack_info;
- return OrSearch::create(std::move(children), strict, unpack_info);
+ return create_or_filter(_terms, strict, constraint);
}
void
diff --git a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
index 79d78c3faab..9a29c4c0caf 100644
--- a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
@@ -83,16 +83,6 @@ need_normal_features_for_children(const IntermediateBlueprint &blueprint, fef::M
}
}
-/** utility for operators that degrade to AND when creating filter */
-SearchIterator::UP createAndFilter(const IntermediateBlueprint &self,
- const std::vector<Blueprint *>& children,
- bool strict, Blueprint::FilterConstraint constraint)
-{
- auto search = Blueprint::create_and_filter(children, strict, constraint);
- static_cast<AndSearch &>(*search).estimate(self.getState().estimate().estHits);
- return search;
-}
-
} // namespace search::queryeval::<unnamed>
//-----------------------------------------------------------------------------
@@ -146,7 +136,7 @@ AndNotBlueprint::get_replacement()
}
void
-AndNotBlueprint::sort(std::vector<Blueprint*> &children) const
+AndNotBlueprint::sort(Children &children) const
{
if (children.size() > 2) {
std::sort(children.begin() + 1, children.end(), TieredGreaterEstimate());
@@ -180,31 +170,10 @@ AndNotBlueprint::createIntermediateSearch(MultiSearch::Children sub_searches,
return AndNotSearch::create(std::move(sub_searches), strict);
}
-namespace {
-Blueprint::FilterConstraint invert(Blueprint::FilterConstraint constraint) {
- if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
- return Blueprint::FilterConstraint::LOWER_BOUND;
- }
- if (constraint == Blueprint::FilterConstraint::LOWER_BOUND) {
- return Blueprint::FilterConstraint::UPPER_BOUND;
- }
- abort();
-}
-} // namespace <unnamed>
-
SearchIterator::UP
AndNotBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- MultiSearch::Children sub_searches;
- sub_searches.reserve(childCnt());
- for (size_t i = 0; i < childCnt(); ++i) {
- bool child_strict = strict && inheritStrict(i);
- auto search = (i == 0)
- ? getChild(i).createFilterSearch(child_strict, constraint)
- : getChild(i).createFilterSearch(child_strict, invert(constraint));
- sub_searches.push_back(std::move(search));
- }
- return AndNotSearch::create(std::move(sub_searches), strict);
+ return create_andnot_filter(get_children(), strict, constraint);
}
//-----------------------------------------------------------------------------
@@ -248,7 +217,7 @@ AndBlueprint::get_replacement()
}
void
-AndBlueprint::sort(std::vector<Blueprint*> &children) const
+AndBlueprint::sort(Children &children) const
{
std::sort(children.begin(), children.end(), TieredLessEstimate());
}
@@ -286,7 +255,7 @@ AndBlueprint::createIntermediateSearch(MultiSearch::Children sub_searches,
SearchIterator::UP
AndBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- return createAndFilter(*this, get_children(), strict, constraint);
+ return create_and_filter(get_children(), strict, constraint);
}
double
@@ -339,7 +308,7 @@ OrBlueprint::get_replacement()
}
void
-OrBlueprint::sort(std::vector<Blueprint*> &children) const
+OrBlueprint::sort(Children &children) const
{
std::sort(children.begin(), children.end(), TieredGreaterEstimate());
}
@@ -372,15 +341,7 @@ OrBlueprint::createIntermediateSearch(MultiSearch::Children sub_searches,
SearchIterator::UP
OrBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- MultiSearch::Children sub_searches;
- sub_searches.reserve(childCnt());
- for (size_t i = 0; i < childCnt(); ++i) {
- bool child_strict = strict && inheritStrict(i);
- auto search = getChild(i).createFilterSearch(child_strict, constraint);
- sub_searches.push_back(std::move(search));
- }
- UnpackInfo unpack_info;
- return OrSearch::create(std::move(sub_searches), strict, unpack_info);
+ return create_or_filter(get_children(), strict, constraint);
}
//-----------------------------------------------------------------------------
@@ -404,7 +365,7 @@ WeakAndBlueprint::exposeFields() const
}
void
-WeakAndBlueprint::sort(std::vector<Blueprint*> &) const
+WeakAndBlueprint::sort(Children &) const
{
// order needs to stay the same as _weights
}
@@ -440,11 +401,7 @@ WeakAndBlueprint::createIntermediateSearch(MultiSearch::Children sub_searches,
SearchIterator::UP
WeakAndBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
- return create_or_filter(get_children(), strict, constraint);
- } else {
- return std::make_unique<EmptySearch>();
- }
+ return create_atmost_or_filter(get_children(), strict, constraint);
}
//-----------------------------------------------------------------------------
@@ -462,7 +419,7 @@ NearBlueprint::exposeFields() const
}
void
-NearBlueprint::sort(std::vector<Blueprint*> &children) const
+NearBlueprint::sort(Children &children) const
{
std::sort(children.begin(), children.end(), TieredLessEstimate());
}
@@ -497,11 +454,7 @@ NearBlueprint::createIntermediateSearch(MultiSearch::Children sub_searches,
SearchIterator::UP
NearBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
- return createAndFilter(*this, get_children(), strict, constraint);
- } else {
- return std::make_unique<EmptySearch>();
- }
+ return create_atmost_and_filter(get_children(), strict, constraint);
}
//-----------------------------------------------------------------------------
@@ -519,7 +472,7 @@ ONearBlueprint::exposeFields() const
}
void
-ONearBlueprint::sort(std::vector<Blueprint*> &children) const
+ONearBlueprint::sort(Children &children) const
{
// ordered near cannot sort children here
(void)children;
@@ -557,11 +510,7 @@ ONearBlueprint::createIntermediateSearch(MultiSearch::Children sub_searches,
SearchIterator::UP
ONearBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
- return createAndFilter(*this, get_children(), strict, constraint);
- } else {
- return std::make_unique<EmptySearch>();
- }
+ return create_atmost_and_filter(get_children(), strict, constraint);
}
//-----------------------------------------------------------------------------
@@ -602,7 +551,7 @@ RankBlueprint::get_replacement()
}
void
-RankBlueprint::sort(std::vector<Blueprint*> &children) const
+RankBlueprint::sort(Children &children) const
{
(void)children;
}
@@ -642,8 +591,7 @@ RankBlueprint::createIntermediateSearch(MultiSearch::Children sub_searches,
SearchIterator::UP
RankBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- assert(childCnt() > 0);
- return getChild(0).createFilterSearch(strict, constraint);
+ return create_first_child_filter(get_children(), strict, constraint);
}
//-----------------------------------------------------------------------------
@@ -666,7 +614,7 @@ SourceBlenderBlueprint::exposeFields() const
}
void
-SourceBlenderBlueprint::sort(std::vector<Blueprint*> &) const
+SourceBlenderBlueprint::sort(Children &) const
{
}
@@ -716,19 +664,7 @@ SourceBlenderBlueprint::createIntermediateSearch(MultiSearch::Children sub_searc
SearchIterator::UP
SourceBlenderBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- if (constraint == FilterConstraint::UPPER_BOUND) {
- MultiSearch::Children sub_searches;
- sub_searches.reserve(childCnt());
- for (size_t i = 0; i < childCnt(); ++i) {
- bool child_strict = strict && inheritStrict(i);
- auto search = getChild(i).createFilterSearch(child_strict, constraint);
- sub_searches.push_back(std::move(search));
- }
- UnpackInfo unpack_info;
- return OrSearch::create(std::move(sub_searches), strict, unpack_info);
- } else {
- return std::make_unique<EmptySearch>();
- }
+ return create_atmost_or_filter(get_children(), strict, constraint);
}
bool
diff --git a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h
index c8bd26fe4a7..f4ae2e21b41 100644
--- a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h
+++ b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h
@@ -20,7 +20,7 @@ public:
void optimize_self() override;
bool isAndNot() const override { return true; }
Blueprint::UP get_replacement() override;
- void sort(std::vector<Blueprint*> &children) const override;
+ void sort(Children &children) const override;
bool inheritStrict(size_t i) const override;
SearchIterator::UP
createIntermediateSearch(MultiSearch::Children subSearches,
@@ -43,7 +43,7 @@ public:
void optimize_self() override;
bool isAnd() const override { return true; }
Blueprint::UP get_replacement() override;
- void sort(std::vector<Blueprint*> &children) const override;
+ void sort(Children &children) const override;
bool inheritStrict(size_t i) const override;
SearchIterator::UP
createIntermediateSearch(MultiSearch::Children subSearches,
@@ -67,7 +67,7 @@ public:
void optimize_self() override;
bool isOr() const override { return true; }
Blueprint::UP get_replacement() override;
- void sort(std::vector<Blueprint*> &children) const override;
+ void sort(Children &children) const override;
bool inheritStrict(size_t i) const override;
SearchIterator::UP
createIntermediateSearch(MultiSearch::Children subSearches,
@@ -87,7 +87,7 @@ private:
public:
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
- void sort(std::vector<Blueprint*> &children) const override;
+ void sort(Children &children) const override;
bool inheritStrict(size_t i) const override;
bool always_needs_unpack() const override;
SearchIterator::UP
@@ -116,7 +116,7 @@ public:
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
bool should_optimize_children() const override { return false; }
- void sort(std::vector<Blueprint*> &children) const override;
+ void sort(Children &children) const override;
bool inheritStrict(size_t i) const override;
SearchIteratorUP createSearch(fef::MatchData &md, bool strict) const override;
SearchIterator::UP
@@ -138,7 +138,7 @@ public:
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
bool should_optimize_children() const override { return false; }
- void sort(std::vector<Blueprint*> &children) const override;
+ void sort(Children &children) const override;
bool inheritStrict(size_t i) const override;
SearchIteratorUP createSearch(fef::MatchData &md, bool strict) const override;
SearchIterator::UP
@@ -158,7 +158,7 @@ public:
FieldSpecBaseList exposeFields() const override;
void optimize_self() override;
Blueprint::UP get_replacement() override;
- void sort(std::vector<Blueprint*> &children) const override;
+ void sort(Children &children) const override;
bool inheritStrict(size_t i) const override;
bool isRank() const override { return true; }
SearchIterator::UP
@@ -179,7 +179,7 @@ public:
SourceBlenderBlueprint(const ISourceSelector &selector);
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
- void sort(std::vector<Blueprint*> &children) const override;
+ void sort(Children &children) const override;
bool inheritStrict(size_t i) const override;
/**
* Will return the index matching the given sourceId.
diff --git a/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.cpp
index e2a22c16631..aeaa0f98c2f 100644
--- a/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/same_element_blueprint.cpp
@@ -93,19 +93,7 @@ SameElementBlueprint::createLeafSearch(const search::fef::TermFieldMatchDataArra
SearchIterator::UP
SameElementBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
- MultiSearch::Children sub_searches;
- sub_searches.reserve(_terms.size());
- for (size_t i = 0; i < _terms.size(); ++i) {
- auto search = _terms[i]->createFilterSearch(strict && (i == 0), constraint);
- sub_searches.push_back(std::move(search));
- }
- UnpackInfo unpack_info;
- auto search = AndSearch::create(std::move(sub_searches), strict, unpack_info);
- return search;
- } else {
- return std::make_unique<EmptySearch>();
- }
+ return create_atmost_and_filter(_terms, strict, constraint);
}
void
diff --git a/searchlib/src/vespa/searchlib/queryeval/simple_phrase_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/simple_phrase_blueprint.cpp
index 2ef7a2cf0a0..9a8e03c0651 100644
--- a/searchlib/src/vespa/searchlib/queryeval/simple_phrase_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/simple_phrase_blueprint.cpp
@@ -23,13 +23,7 @@ SimplePhraseBlueprint::SimplePhraseBlueprint(const FieldSpec &field, bool expens
}
}
-SimplePhraseBlueprint::~SimplePhraseBlueprint()
-{
- while (!_terms.empty()) {
- delete _terms.back();
- _terms.pop_back();
- }
-}
+SimplePhraseBlueprint::~SimplePhraseBlueprint() = default;
FieldSpec
SimplePhraseBlueprint::getNextChildField(const FieldSpec &outer)
@@ -51,7 +45,7 @@ SimplePhraseBlueprint::addTerm(Blueprint::UP term)
_estimate = childEst;
}
setEstimate(_estimate);
- _terms.push_back(term.release());
+ _terms.push_back(std::move(term));
}
SearchIterator::UP
@@ -87,18 +81,7 @@ SimplePhraseBlueprint::createLeafSearch(const fef::TermFieldMatchDataArray &tfmd
SearchIterator::UP
SimplePhraseBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- if (constraint == FilterConstraint::UPPER_BOUND) {
- MultiSearch::Children children;
- children.reserve(_terms.size());
- for (size_t i = 0; i < _terms.size(); ++i) {
- bool child_strict = strict && (i == 0);
- children.push_back(_terms[i]->createFilterSearch(child_strict, constraint));
- }
- UnpackInfo unpack_info;
- return AndSearch::create(std::move(children), strict, unpack_info);
- } else {
- return std::make_unique<EmptySearch>();
- }
+ return create_atmost_and_filter(_terms, strict, constraint);
}
void
diff --git a/searchlib/src/vespa/searchlib/queryeval/simple_phrase_blueprint.h b/searchlib/src/vespa/searchlib/queryeval/simple_phrase_blueprint.h
index 0cfb2357321..c4faf3951f6 100644
--- a/searchlib/src/vespa/searchlib/queryeval/simple_phrase_blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/simple_phrase_blueprint.h
@@ -12,10 +12,10 @@ namespace search::queryeval {
class SimplePhraseBlueprint : public ComplexLeafBlueprint
{
private:
- FieldSpec _field;
- HitEstimate _estimate;
- fef::MatchDataLayout _layout;
- std::vector<Blueprint*> _terms;
+ FieldSpec _field;
+ HitEstimate _estimate;
+ fef::MatchDataLayout _layout;
+ std::vector<Blueprint::UP> _terms;
public:
SimplePhraseBlueprint(const FieldSpec &field, bool expensive);
diff --git a/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.cpp
index 961c8785b40..fe212666ec9 100644
--- a/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.cpp
@@ -47,13 +47,7 @@ ParallelWeakAndBlueprint::ParallelWeakAndBlueprint(const FieldSpec &field,
{
}
-ParallelWeakAndBlueprint::~ParallelWeakAndBlueprint()
-{
- while (!_terms.empty()) {
- delete _terms.back();
- _terms.pop_back();
- }
-}
+ParallelWeakAndBlueprint::~ParallelWeakAndBlueprint() = default;
FieldSpec
ParallelWeakAndBlueprint::getNextChildField(const FieldSpec &outer)
@@ -74,8 +68,7 @@ ParallelWeakAndBlueprint::addTerm(Blueprint::UP term, int32_t weight)
setEstimate(_estimate);
}
_weights.push_back(weight);
- _terms.push_back(term.get());
- term.release();
+ _terms.push_back(std::move(term));
set_tree_size(_terms.size() + 1);
}
@@ -107,11 +100,7 @@ ParallelWeakAndBlueprint::createLeafSearch(const search::fef::TermFieldMatchData
std::unique_ptr<SearchIterator>
ParallelWeakAndBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) const
{
- if (constraint == Blueprint::FilterConstraint::UPPER_BOUND) {
- return create_or_filter(_terms, strict, constraint);
- } else {
- return std::make_unique<EmptySearch>();
- }
+ return create_atmost_or_filter(_terms, strict, constraint);
}
void
diff --git a/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h b/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h
index cf0b6c1b254..39d2b5a03f0 100644
--- a/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/wand/parallel_weak_and_blueprint.h
@@ -29,7 +29,7 @@ private:
HitEstimate _estimate;
fef::MatchDataLayout _layout;
std::vector<int32_t> _weights;
- std::vector<Blueprint*> _terms;
+ std::vector<Blueprint::UP> _terms;
ParallelWeakAndBlueprint(const ParallelWeakAndBlueprint &);
ParallelWeakAndBlueprint &operator=(const ParallelWeakAndBlueprint &);
diff --git a/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.cpp
index 4862d9a2375..f855b72812a 100644
--- a/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.cpp
@@ -15,17 +15,17 @@ class WeightedSetTermMatchingElementsSearch : public MatchingElementsSearch
fef::TermFieldMatchData _tfmd;
fef::TermFieldMatchDataArray _tfmda;
vespalib::string _field_name;
- const std::vector<Blueprint*>& _terms;
+ const std::vector<Blueprint::UP> &_terms;
std::unique_ptr<WeightedSetTermSearch> _search;
public:
- WeightedSetTermMatchingElementsSearch(const WeightedSetTermBlueprint& bp, const vespalib::string& field_name, const std::vector<Blueprint*>& terms);
+ WeightedSetTermMatchingElementsSearch(const WeightedSetTermBlueprint& bp, const vespalib::string& field_name, const std::vector<Blueprint::UP> &terms);
~WeightedSetTermMatchingElementsSearch() override;
void find_matching_elements(uint32_t docid, MatchingElements& result) override;
void initRange(uint32_t begin_id, uint32_t end_id) override;
};
-WeightedSetTermMatchingElementsSearch::WeightedSetTermMatchingElementsSearch(const WeightedSetTermBlueprint& bp, const vespalib::string& field_name, const std::vector<Blueprint*>& terms)
+WeightedSetTermMatchingElementsSearch::WeightedSetTermMatchingElementsSearch(const WeightedSetTermBlueprint& bp, const vespalib::string& field_name, const std::vector<Blueprint::UP> &terms)
: _tfmd(),
_tfmda(),
_field_name(field_name),
@@ -71,13 +71,7 @@ WeightedSetTermBlueprint::WeightedSetTermBlueprint(const FieldSpec &field)
set_allow_termwise_eval(true);
}
-WeightedSetTermBlueprint::~WeightedSetTermBlueprint()
-{
- while (!_terms.empty()) {
- delete _terms.back();
- _terms.pop_back();
- }
-}
+WeightedSetTermBlueprint::~WeightedSetTermBlueprint() = default;
void
WeightedSetTermBlueprint::addTerm(Blueprint::UP term, int32_t weight)
@@ -92,8 +86,7 @@ WeightedSetTermBlueprint::addTerm(Blueprint::UP term, int32_t weight)
setEstimate(_estimate);
}
_weights.push_back(weight);
- _terms.push_back(term.get());
- term.release();
+ _terms.push_back(std::move(term));
}
diff --git a/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.h b/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.h
index a840310d3d9..2a3db3ec52d 100644
--- a/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_blueprint.h
@@ -12,11 +12,11 @@ namespace search::queryeval {
class WeightedSetTermBlueprint : public ComplexLeafBlueprint
{
- HitEstimate _estimate;
- fef::MatchDataLayout _layout;
- FieldSpec _children_field;
- std::vector<int32_t> _weights;
- std::vector<Blueprint*> _terms;
+ HitEstimate _estimate;
+ fef::MatchDataLayout _layout;
+ FieldSpec _children_field;
+ std::vector<int32_t> _weights;
+ std::vector<Blueprint::UP> _terms;
public:
WeightedSetTermBlueprint(const FieldSpec &field);
@@ -37,7 +37,7 @@ public:
std::unique_ptr<MatchingElementsSearch> create_matching_elements_search(const MatchingElementsFields &fields) const override;
void visitMembers(vespalib::ObjectVisitor &visitor) const override;
const vespalib::string &field_name() const { return _children_field.getName(); }
- const std::vector<Blueprint*>& get_terms() const { return _terms; }
+ const std::vector<Blueprint::UP> &get_terms() const { return _terms; }
private:
void fetchPostings(const ExecuteInfo &execInfo) override;
diff --git a/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_search.cpp b/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_search.cpp
index 0eebfb9f690..375f570160a 100644
--- a/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_search.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_search.cpp
@@ -50,7 +50,7 @@ private:
void seek_child(ref_t child, uint32_t docId) {
_termPos[child] = _children.seek(child, docId);
}
- void get_matching_elements_child(ref_t child, uint32_t docId, const std::vector<Blueprint *> &child_blueprints, std::vector<uint32_t> &dst) {
+ void get_matching_elements_child(ref_t child, uint32_t docId, const std::vector<Blueprint::UP> &child_blueprints, std::vector<uint32_t> &dst) {
auto *sc = child_blueprints[child]->get_attribute_search_context();
if (sc != nullptr) {
int32_t weight(0);
@@ -151,7 +151,7 @@ public:
void and_hits_into(BitVector &result, uint32_t begin_id) override {
result.andWith(*get_hits(begin_id));
}
- void find_matching_elements(uint32_t docId, const std::vector<Blueprint *>& child_blueprints, std::vector<uint32_t> &dst) override {
+ void find_matching_elements(uint32_t docId, const std::vector<Blueprint::UP> &child_blueprints, std::vector<uint32_t> &dst) override {
pop_matching_children(docId);
for (ref_t *ptr = _data_stash; ptr < _data_end; ++ptr) {
get_matching_elements_child(*ptr, docId, child_blueprints, dst);
diff --git a/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_search.h b/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_search.h
index 3aaf3477bbd..e3e12c27f28 100644
--- a/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_search.h
+++ b/searchlib/src/vespa/searchlib/queryeval/weighted_set_term_search.h
@@ -44,7 +44,7 @@ public:
// used during docsum fetching to identify matching elements
// initRange must be called before use.
// doSeek/doUnpack must not be called.
- virtual void find_matching_elements(uint32_t docid, const std::vector<Blueprint *>& child_blueprints, std::vector<uint32_t> &dst) = 0;
+ virtual void find_matching_elements(uint32_t docid, const std::vector<std::unique_ptr<Blueprint>> &child_blueprints, std::vector<uint32_t> &dst) = 0;
};
} // namespace search::queryeval
diff --git a/searchlib/src/vespa/searchlib/tensor/bitvector_visited_tracker.cpp b/searchlib/src/vespa/searchlib/tensor/bitvector_visited_tracker.cpp
index 524cc2b9cd5..e456e87dc96 100644
--- a/searchlib/src/vespa/searchlib/tensor/bitvector_visited_tracker.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/bitvector_visited_tracker.cpp
@@ -4,8 +4,8 @@
namespace search::tensor {
-BitVectorVisitedTracker::BitVectorVisitedTracker(const HnswIndex&, uint32_t doc_id_limit, uint32_t)
- : _visited(doc_id_limit)
+BitVectorVisitedTracker::BitVectorVisitedTracker(const HnswIndex&, uint32_t nodeid_limit, uint32_t)
+ : _visited(nodeid_limit)
{
}
diff --git a/searchlib/src/vespa/searchlib/tensor/bitvector_visited_tracker.h b/searchlib/src/vespa/searchlib/tensor/bitvector_visited_tracker.h
index bbd49aa0482..ecdd957187f 100644
--- a/searchlib/src/vespa/searchlib/tensor/bitvector_visited_tracker.h
+++ b/searchlib/src/vespa/searchlib/tensor/bitvector_visited_tracker.h
@@ -16,14 +16,14 @@ class BitVectorVisitedTracker
{
search::AllocatedBitVector _visited;
public:
- BitVectorVisitedTracker(const HnswIndex&, uint32_t doc_id_limit, uint32_t);
+ BitVectorVisitedTracker(const HnswIndex&, uint32_t nodeid_limit, uint32_t);
~BitVectorVisitedTracker();
- void mark(uint32_t doc_id) { _visited.setBit(doc_id); }
- bool try_mark(uint32_t doc_id) {
- if (_visited.testBit(doc_id)) {
+ void mark(uint32_t nodeid) { _visited.setBit(nodeid); }
+ bool try_mark(uint32_t nodeid) {
+ if (_visited.testBit(nodeid)) {
return false;
} else {
- _visited.setBit(doc_id);
+ _visited.setBit(nodeid);
return true;
}
}
diff --git a/searchlib/src/vespa/searchlib/tensor/hash_set_visited_tracker.h b/searchlib/src/vespa/searchlib/tensor/hash_set_visited_tracker.h
index c2a14235f47..30434d1029d 100644
--- a/searchlib/src/vespa/searchlib/tensor/hash_set_visited_tracker.h
+++ b/searchlib/src/vespa/searchlib/tensor/hash_set_visited_tracker.h
@@ -18,9 +18,9 @@ class HashSetVisitedTracker
public:
HashSetVisitedTracker(const HnswIndex&, uint32_t, uint32_t estimated_visited_nodes);
~HashSetVisitedTracker();
- void mark(uint32_t doc_id) { _visited.insert(doc_id); }
- bool try_mark(uint32_t doc_id) {
- return _visited.insert(doc_id).second;
+ void mark(uint32_t nodeid) { _visited.insert(nodeid); }
+ bool try_mark(uint32_t nodeid) {
+ return _visited.insert(nodeid).second;
}
};
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_graph.cpp b/searchlib/src/vespa/searchlib/tensor/hnsw_graph.cpp
index 56af2ed3b35..6d8b143cff9 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_graph.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_graph.cpp
@@ -11,7 +11,7 @@ HnswGraph::HnswGraph()
node_refs_size(1u),
nodes(HnswIndex::make_default_node_store_config(), {}),
links(HnswIndex::make_default_link_store_config(), {}),
- entry_docid_and_level()
+ entry_nodeid_and_level()
{
node_refs.ensure_size(1, AtomicEntryRef());
EntryNode entry;
@@ -21,36 +21,36 @@ HnswGraph::HnswGraph()
HnswGraph::~HnswGraph() = default;
HnswGraph::NodeRef
-HnswGraph::make_node_for_document(uint32_t docid, uint32_t num_levels)
+HnswGraph::make_node(uint32_t nodeid, uint32_t num_levels)
{
- node_refs.ensure_size(docid + 1, AtomicEntryRef());
+ node_refs.ensure_size(nodeid + 1, AtomicEntryRef());
// A document cannot be added twice.
- assert(!get_node_ref(docid).valid());
+ assert(!get_node_ref(nodeid).valid());
// Note: The level array instance lives as long as the document is present in the index.
std::vector<AtomicEntryRef> levels(num_levels, AtomicEntryRef());
auto node_ref = nodes.add(levels);
- node_refs[docid].store_release(node_ref);
- if (docid >= node_refs_size.load(std::memory_order_relaxed)) {
- node_refs_size.store(docid + 1, std::memory_order_release);
+ node_refs[nodeid].store_release(node_ref);
+ if (nodeid >= node_refs_size.load(std::memory_order_relaxed)) {
+ node_refs_size.store(nodeid + 1, std::memory_order_release);
}
return node_ref;
}
void
-HnswGraph::remove_node_for_document(uint32_t docid)
+HnswGraph::remove_node(uint32_t nodeid)
{
- auto node_ref = get_node_ref(docid);
+ auto node_ref = get_node_ref(nodeid);
assert(node_ref.valid());
auto levels = nodes.get(node_ref);
vespalib::datastore::EntryRef invalid;
- node_refs[docid].store_release(invalid);
+ node_refs[nodeid].store_release(invalid);
// Ensure data referenced through the old ref can be recycled:
nodes.remove(node_ref);
for (size_t i = 0; i < levels.size(); ++i) {
auto old_links_ref = levels[i].load_relaxed();
links.remove(old_links_ref);
}
- if (docid + 1 == node_refs_size.load(std::memory_order_relaxed)) {
+ if (nodeid + 1 == node_refs_size.load(std::memory_order_relaxed)) {
trim_node_refs_size();
}
}
@@ -58,18 +58,18 @@ HnswGraph::remove_node_for_document(uint32_t docid)
void
HnswGraph::trim_node_refs_size()
{
- uint32_t check_doc_id = node_refs_size.load(std::memory_order_relaxed) - 1;
- while (check_doc_id > 0u && !get_node_ref(check_doc_id).valid()) {
- --check_doc_id;
+ uint32_t check_nodeid = node_refs_size.load(std::memory_order_relaxed) - 1;
+ while (check_nodeid > 0u && !get_node_ref(check_nodeid).valid()) {
+ --check_nodeid;
}
- node_refs_size.store(check_doc_id + 1, std::memory_order_release);
+ node_refs_size.store(check_nodeid + 1, std::memory_order_release);
}
void
-HnswGraph::set_link_array(uint32_t docid, uint32_t level, const LinkArrayRef& new_links)
+HnswGraph::set_link_array(uint32_t nodeid, uint32_t level, const LinkArrayRef& new_links)
{
auto new_links_ref = links.add(new_links);
- auto node_ref = get_node_ref(docid);
+ auto node_ref = get_node_ref(nodeid);
assert(node_ref.valid());
auto levels = nodes.get_writable(node_ref);
assert(level < levels.size());
@@ -112,15 +112,15 @@ void
HnswGraph::set_entry_node(EntryNode node) {
uint64_t value = node.level;
value <<= 32;
- value |= node.docid;
+ value |= node.nodeid;
if (node.node_ref.valid()) {
assert(node.level >= 0);
- assert(node.docid > 0);
+ assert(node.nodeid > 0);
} else {
assert(node.level == -1);
- assert(node.docid == 0);
+ assert(node.nodeid == 0);
}
- entry_docid_and_level.store(value, std::memory_order_release);
+ entry_nodeid_and_level.store(value, std::memory_order_release);
}
} // namespace
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_graph.h b/searchlib/src/vespa/searchlib/tensor/hnsw_graph.h
index 9e3850bd470..60ba7d55f7f 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_graph.h
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_graph.h
@@ -43,27 +43,27 @@ struct HnswGraph {
NodeStore nodes;
LinkStore links;
- std::atomic<uint64_t> entry_docid_and_level;
+ std::atomic<uint64_t> entry_nodeid_and_level;
HnswGraph();
~HnswGraph();
- NodeRef make_node_for_document(uint32_t docid, uint32_t num_levels);
+ NodeRef make_node(uint32_t nodeid, uint32_t num_levels);
- void remove_node_for_document(uint32_t docid);
+ void remove_node(uint32_t nodeid);
void trim_node_refs_size();
- NodeRef get_node_ref(uint32_t docid) const {
- return node_refs.get_elem_ref(docid).load_relaxed(); // Called from writer only
+ NodeRef get_node_ref(uint32_t nodeid) const {
+ return node_refs.get_elem_ref(nodeid).load_relaxed(); // Called from writer only
}
- NodeRef acquire_node_ref(uint32_t docid) const {
- return node_refs.acquire_elem_ref(docid).load_acquire();
+ NodeRef acquire_node_ref(uint32_t nodeid) const {
+ return node_refs.acquire_elem_ref(nodeid).load_acquire();
}
- bool still_valid(uint32_t docid, NodeRef node_ref) const {
- return node_ref.valid() && (acquire_node_ref(docid) == node_ref);
+ bool still_valid(uint32_t nodeid, NodeRef node_ref) const {
+ return node_ref.valid() && (acquire_node_ref(nodeid) == node_ref);
}
LevelArrayRef get_level_array(NodeRef node_ref) const {
@@ -73,13 +73,13 @@ struct HnswGraph {
return LevelArrayRef();
}
- LevelArrayRef get_level_array(uint32_t docid) const {
- auto node_ref = get_node_ref(docid);
+ LevelArrayRef get_level_array(uint32_t nodeid) const {
+ auto node_ref = get_node_ref(nodeid);
return get_level_array(node_ref);
}
- LevelArrayRef acquire_level_array(uint32_t docid) const {
- auto node_ref = acquire_node_ref(docid);
+ LevelArrayRef acquire_level_array(uint32_t nodeid) const {
+ auto node_ref = acquire_node_ref(nodeid);
return get_level_array(node_ref);
}
@@ -93,13 +93,13 @@ struct HnswGraph {
return LinkArrayRef();
}
- LinkArrayRef get_link_array(uint32_t docid, uint32_t level) const {
- auto levels = get_level_array(docid);
+ LinkArrayRef get_link_array(uint32_t nodeid, uint32_t level) const {
+ auto levels = get_level_array(nodeid);
return get_link_array(levels, level);
}
- LinkArrayRef acquire_link_array(uint32_t docid, uint32_t level) const {
- auto levels = acquire_level_array(docid);
+ LinkArrayRef acquire_link_array(uint32_t nodeid, uint32_t level) const {
+ auto levels = acquire_level_array(nodeid);
return get_link_array(levels, level);
}
@@ -108,19 +108,19 @@ struct HnswGraph {
return get_link_array(levels, level);
}
- void set_link_array(uint32_t docid, uint32_t level, const LinkArrayRef& new_links);
+ void set_link_array(uint32_t nodeid, uint32_t level, const LinkArrayRef& new_links);
struct EntryNode {
- uint32_t docid;
+ uint32_t nodeid;
NodeRef node_ref;
int32_t level;
EntryNode()
- : docid(0), // Note that docid 0 is reserved and never used
+ : nodeid(0), // Note that nodeid 0 is reserved and never used
node_ref(),
level(-1)
{}
- EntryNode(uint32_t docid_in, NodeRef node_ref_in, int32_t level_in)
- : docid(docid_in),
+ EntryNode(uint32_t nodeid_in, NodeRef node_ref_in, int32_t level_in)
+ : nodeid(nodeid_in),
node_ref(node_ref_in),
level(level_in)
{}
@@ -129,24 +129,24 @@ struct HnswGraph {
void set_entry_node(EntryNode node);
uint64_t get_entry_atomic() const {
- return entry_docid_and_level.load(std::memory_order_acquire);
+ return entry_nodeid_and_level.load(std::memory_order_acquire);
}
EntryNode get_entry_node() const {
EntryNode entry;
while (true) {
uint64_t value = get_entry_atomic();
- entry.docid = (uint32_t)value;
- entry.node_ref = acquire_node_ref(entry.docid);
+ entry.nodeid = (uint32_t)value;
+ entry.node_ref = acquire_node_ref(entry.nodeid);
entry.level = (int32_t)(value >> 32);
- if ((entry.docid == 0)
+ if ((entry.nodeid == 0)
&& (entry.level == -1)
&& ! entry.node_ref.valid())
{
// invalid in every way
return entry;
}
- if ((entry.docid > 0)
+ if ((entry.nodeid > 0)
&& (entry.level > -1)
&& entry.node_ref.valid()
&& (get_entry_atomic() == value))
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp b/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp
index a10ff1c48dc..89b4f62146c 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp
@@ -86,7 +86,7 @@ bool
HnswIndex::have_closer_distance(HnswCandidate candidate, const HnswCandidateVector& result) const
{
for (const auto & neighbor : result) {
- double dist = calc_distance(candidate.docid, neighbor.docid);
+ double dist = calc_distance(candidate.nodeid, neighbor.nodeid);
if (dist < candidate.distance) {
return true;
}
@@ -104,7 +104,7 @@ HnswIndex::select_neighbors_simple(const HnswCandidateVector& neighbors, uint32_
if (result.used.size() < max_links) {
result.used.push_back(candidate);
} else {
- result.unused.push_back(candidate.docid);
+ result.unused.push_back(candidate.nodeid);
}
}
return result;
@@ -122,7 +122,7 @@ HnswIndex::select_neighbors_heuristic(const HnswCandidateVector& neighbors, uint
auto candidate = nearest.top();
nearest.pop();
if (have_closer_distance(candidate, result.used)) {
- result.unused.push_back(candidate.docid);
+ result.unused.push_back(candidate.nodeid);
continue;
}
result.used.push_back(candidate);
@@ -130,7 +130,7 @@ HnswIndex::select_neighbors_heuristic(const HnswCandidateVector& neighbors, uint
while (!nearest.empty()) {
candidate = nearest.top();
nearest.pop();
- result.unused.push_back(candidate.docid);
+ result.unused.push_back(candidate.nodeid);
}
}
}
@@ -148,40 +148,40 @@ HnswIndex::select_neighbors(const HnswCandidateVector& neighbors, uint32_t max_l
}
void
-HnswIndex::shrink_if_needed(uint32_t docid, uint32_t level)
+HnswIndex::shrink_if_needed(uint32_t nodeid, uint32_t level)
{
- auto old_links = _graph.get_link_array(docid, level);
+ auto old_links = _graph.get_link_array(nodeid, level);
uint32_t max_links = max_links_for_level(level);
if (old_links.size() > max_links) {
HnswCandidateVector neighbors;
neighbors.reserve(old_links.size());
- for (uint32_t neighbor_docid : old_links) {
- double dist = calc_distance(docid, neighbor_docid);
- neighbors.emplace_back(neighbor_docid, dist);
+ for (uint32_t neighbor_nodeid : old_links) {
+ double dist = calc_distance(nodeid, neighbor_nodeid);
+ neighbors.emplace_back(neighbor_nodeid, dist);
}
auto split = select_neighbors(neighbors, max_links);
LinkArray new_links;
new_links.reserve(split.used.size());
for (const auto & neighbor : split.used) {
- new_links.push_back(neighbor.docid);
+ new_links.push_back(neighbor.nodeid);
}
- _graph.set_link_array(docid, level, new_links);
- for (uint32_t removed_docid : split.unused) {
- remove_link_to(removed_docid, docid, level);
+ _graph.set_link_array(nodeid, level, new_links);
+ for (uint32_t removed_nodeid : split.unused) {
+ remove_link_to(removed_nodeid, nodeid, level);
}
}
}
void
-HnswIndex::connect_new_node(uint32_t docid, const LinkArrayRef &neighbors, uint32_t level)
+HnswIndex::connect_new_node(uint32_t nodeid, const LinkArrayRef &neighbors, uint32_t level)
{
- _graph.set_link_array(docid, level, neighbors);
- for (uint32_t neighbor_docid : neighbors) {
- auto old_links = _graph.get_link_array(neighbor_docid, level);
- add_link_to(neighbor_docid, level, old_links, docid);
+ _graph.set_link_array(nodeid, level, neighbors);
+ for (uint32_t neighbor_nodeid : neighbors) {
+ auto old_links = _graph.get_link_array(neighbor_nodeid, level);
+ add_link_to(neighbor_nodeid, level, old_links, nodeid);
}
- for (uint32_t neighbor_docid : neighbors) {
- shrink_if_needed(neighbor_docid, level);
+ for (uint32_t neighbor_nodeid : neighbors) {
+ shrink_if_needed(neighbor_nodeid, level);
}
}
@@ -199,38 +199,38 @@ HnswIndex::remove_link_to(uint32_t remove_from, uint32_t remove_id, uint32_t lev
double
-HnswIndex::calc_distance(uint32_t lhs_docid, uint32_t rhs_docid) const
+HnswIndex::calc_distance(uint32_t lhs_nodeid, uint32_t rhs_nodeid) const
{
- auto lhs = get_vector(lhs_docid);
- return calc_distance(lhs, rhs_docid);
+ auto lhs = get_vector(lhs_nodeid);
+ return calc_distance(lhs, rhs_nodeid);
}
double
-HnswIndex::calc_distance(const TypedCells& lhs, uint32_t rhs_docid) const
+HnswIndex::calc_distance(const TypedCells& lhs, uint32_t rhs_nodeid) const
{
- auto rhs = get_vector(rhs_docid);
+ auto rhs = get_vector(rhs_nodeid);
return _distance_func->calc(lhs, rhs);
}
uint32_t
-HnswIndex::estimate_visited_nodes(uint32_t level, uint32_t doc_id_limit, uint32_t neighbors_to_find, const GlobalFilter* filter) const
+HnswIndex::estimate_visited_nodes(uint32_t level, uint32_t nodeid_limit, uint32_t neighbors_to_find, const GlobalFilter* filter) const
{
uint32_t m_for_level = max_links_for_level(level);
uint64_t base_estimate = uint64_t(m_for_level) * neighbors_to_find + 100;
- if (base_estimate >= doc_id_limit) {
- return doc_id_limit;
+ if (base_estimate >= nodeid_limit) {
+ return nodeid_limit;
}
if (!filter) {
return base_estimate;
}
uint32_t true_bits = filter->count();
if (true_bits == 0) {
- return doc_id_limit;
+ return nodeid_limit;
}
double scaler = double(filter->size()) / true_bits;
double scaled_estimate = scaler * base_estimate;
- if (scaled_estimate >= doc_id_limit) {
- return doc_id_limit;
+ if (scaled_estimate >= nodeid_limit) {
+ return nodeid_limit;
}
return scaled_estimate;
}
@@ -242,13 +242,13 @@ HnswIndex::find_nearest_in_layer(const TypedCells& input, const HnswCandidate& e
bool keep_searching = true;
while (keep_searching) {
keep_searching = false;
- for (uint32_t neighbor_docid : _graph.get_link_array(nearest.node_ref, level)) {
- auto neighbor_ref = _graph.acquire_node_ref(neighbor_docid);
- double dist = calc_distance(input, neighbor_docid);
- if (_graph.still_valid(neighbor_docid, neighbor_ref)
+ for (uint32_t neighbor_nodeid : _graph.get_link_array(nearest.node_ref, level)) {
+ auto neighbor_ref = _graph.acquire_node_ref(neighbor_nodeid);
+ double dist = calc_distance(input, neighbor_nodeid);
+ if (_graph.still_valid(neighbor_nodeid, neighbor_ref)
&& dist < nearest.distance)
{
- nearest = HnswCandidate(neighbor_docid, neighbor_ref, dist);
+ nearest = HnswCandidate(neighbor_nodeid, neighbor_ref, dist);
keep_searching = true;
}
}
@@ -260,17 +260,17 @@ template <class VisitedTracker>
void
HnswIndex::search_layer_helper(const TypedCells& input, uint32_t neighbors_to_find,
FurthestPriQ& best_neighbors, uint32_t level, const GlobalFilter *filter,
- uint32_t doc_id_limit, uint32_t estimated_visited_nodes) const
+ uint32_t nodeid_limit, uint32_t estimated_visited_nodes) const
{
NearestPriQ candidates;
- VisitedTracker visited(*this, doc_id_limit, estimated_visited_nodes);
+ VisitedTracker visited(*this, nodeid_limit, estimated_visited_nodes);
for (const auto &entry : best_neighbors.peek()) {
- if (entry.docid >= doc_id_limit) {
+ if (entry.nodeid >= nodeid_limit) {
continue;
}
candidates.push(entry);
- visited.mark(entry.docid);
- if (filter && !filter->check(entry.docid)) {
+ visited.mark(entry.nodeid);
+ if (filter && !filter->check(entry.nodeid)) {
assert(best_neighbors.size() == 1);
best_neighbors.pop();
}
@@ -283,21 +283,21 @@ HnswIndex::search_layer_helper(const TypedCells& input, uint32_t neighbors_to_fi
break;
}
candidates.pop();
- for (uint32_t neighbor_docid : _graph.get_link_array(cand.node_ref, level)) {
- if (neighbor_docid >= doc_id_limit) {
+ for (uint32_t neighbor_nodeid : _graph.get_link_array(cand.node_ref, level)) {
+ if (neighbor_nodeid >= nodeid_limit) {
continue;
}
- auto neighbor_ref = _graph.acquire_node_ref(neighbor_docid);
+ auto neighbor_ref = _graph.acquire_node_ref(neighbor_nodeid);
if ((! neighbor_ref.valid())
- || ! visited.try_mark(neighbor_docid))
+ || ! visited.try_mark(neighbor_nodeid))
{
continue;
}
- double dist_to_input = calc_distance(input, neighbor_docid);
+ double dist_to_input = calc_distance(input, neighbor_nodeid);
if (dist_to_input < limit_dist) {
- candidates.emplace(neighbor_docid, neighbor_ref, dist_to_input);
- if ((!filter) || filter->check(neighbor_docid)) {
- best_neighbors.emplace(neighbor_docid, neighbor_ref, dist_to_input);
+ candidates.emplace(neighbor_nodeid, neighbor_ref, dist_to_input);
+ if ((!filter) || filter->check(neighbor_nodeid)) {
+ best_neighbors.emplace(neighbor_nodeid, neighbor_ref, dist_to_input);
if (best_neighbors.size() > neighbors_to_find) {
best_neighbors.pop();
limit_dist = best_neighbors.top().distance;
@@ -312,15 +312,15 @@ void
HnswIndex::search_layer(const TypedCells& input, uint32_t neighbors_to_find,
FurthestPriQ& best_neighbors, uint32_t level, const GlobalFilter *filter) const
{
- uint32_t doc_id_limit = _graph.node_refs_size.load(std::memory_order_acquire);
+ uint32_t nodeid_limit = _graph.node_refs_size.load(std::memory_order_acquire);
if (filter) {
- doc_id_limit = std::min(filter->size(), doc_id_limit);
+ nodeid_limit = std::min(filter->size(), nodeid_limit);
}
- uint32_t estimated_visited_nodes = estimate_visited_nodes(level, doc_id_limit, neighbors_to_find, filter);
- if (estimated_visited_nodes >= doc_id_limit / 128) {
- search_layer_helper<BitVectorVisitedTracker>(input, neighbors_to_find, best_neighbors, level, filter, doc_id_limit, estimated_visited_nodes);
+ uint32_t estimated_visited_nodes = estimate_visited_nodes(level, nodeid_limit, neighbors_to_find, filter);
+ if (estimated_visited_nodes >= nodeid_limit / 128) {
+ search_layer_helper<BitVectorVisitedTracker>(input, neighbors_to_find, best_neighbors, level, filter, nodeid_limit, estimated_visited_nodes);
} else {
- search_layer_helper<HashSetVisitedTracker>(input, neighbors_to_find, best_neighbors, level, filter, doc_id_limit, estimated_visited_nodes);
+ search_layer_helper<HashSetVisitedTracker>(input, neighbors_to_find, best_neighbors, level, filter, nodeid_limit, estimated_visited_nodes);
}
}
@@ -342,7 +342,7 @@ void
HnswIndex::add_document(uint32_t docid)
{
vespalib::GenerationHandler::Guard no_guard_needed;
- PreparedAddDoc op = internal_prepare_add(docid, get_vector(docid), no_guard_needed);
+ PreparedAddDoc op = internal_prepare_add(docid, get_vector_by_docid(docid), no_guard_needed);
internal_complete_add(docid, op);
}
@@ -353,14 +353,14 @@ HnswIndex::internal_prepare_add(uint32_t docid, TypedCells input_vector, vespali
int level = _level_generator->max_level();
PreparedAddDoc op(docid, level, std::move(read_guard));
auto entry = _graph.get_entry_node();
- if (entry.docid == 0) {
+ if (entry.nodeid == 0) {
// graph has no entry point
return op;
}
int search_level = entry.level;
- double entry_dist = calc_distance(input_vector, entry.docid);
- // TODO: check if entry docid/node_ref is still valid here
- HnswCandidate entry_point(entry.docid, entry.node_ref, entry_dist);
+ double entry_dist = calc_distance(input_vector, entry.nodeid);
+ // TODO: check if entry nodeid/node_ref is still valid here
+ HnswCandidate entry_point(entry.nodeid, entry.node_ref, entry_dist);
while (search_level > op.max_level) {
entry_point = find_nearest_in_layer(input_vector, entry_point, search_level);
--search_level;
@@ -378,10 +378,10 @@ HnswIndex::internal_prepare_add(uint32_t docid, TypedCells input_vector, vespali
for (const auto & neighbor : neighbors.used) {
auto neighbor_levels = _graph.get_level_array(neighbor.node_ref);
if (size_t(search_level) < neighbor_levels.size()) {
- op.connections[search_level].emplace_back(neighbor.docid, neighbor.node_ref);
+ op.connections[search_level].emplace_back(neighbor.nodeid, neighbor.node_ref);
} else {
LOG(warning, "in prepare_add(%u), selected neighbor %u is missing level %d (has %zu levels)",
- docid, neighbor.docid, search_level, neighbor_levels.size());
+ docid, neighbor.nodeid, search_level, neighbor_levels.size());
}
}
--search_level;
@@ -390,18 +390,18 @@ HnswIndex::internal_prepare_add(uint32_t docid, TypedCells input_vector, vespali
}
HnswIndex::LinkArray
-HnswIndex::filter_valid_docids(uint32_t level, const PreparedAddDoc::Links &neighbors, uint32_t self_docid)
+HnswIndex::filter_valid_nodeids(uint32_t level, const PreparedAddDoc::Links &neighbors, uint32_t self_nodeid)
{
LinkArray valid;
valid.reserve(neighbors.size());
for (const auto & neighbor : neighbors) {
- uint32_t docid = neighbor.first;
+ uint32_t nodeid = neighbor.first;
HnswGraph::NodeRef node_ref = neighbor.second;
- if (_graph.still_valid(docid, node_ref)) {
- assert(docid != self_docid);
+ if (_graph.still_valid(nodeid, node_ref)) {
+ assert(nodeid != self_nodeid);
auto levels = _graph.get_level_array(node_ref);
if (level < levels.size()) {
- valid.push_back(docid);
+ valid.push_back(nodeid);
}
}
}
@@ -411,13 +411,14 @@ HnswIndex::filter_valid_docids(uint32_t level, const PreparedAddDoc::Links &neig
void
HnswIndex::internal_complete_add(uint32_t docid, PreparedAddDoc &op)
{
- auto node_ref = _graph.make_node_for_document(docid, op.max_level + 1);
+ auto nodeid = get_nodeid(docid);
+ auto node_ref = _graph.make_node(nodeid, op.max_level + 1);
for (int level = 0; level <= op.max_level; ++level) {
- auto neighbors = filter_valid_docids(level, op.connections[level], docid);
- connect_new_node(docid, neighbors, level);
+ auto neighbors = filter_valid_nodeids(level, op.connections[level], nodeid);
+ connect_new_node(nodeid, neighbors, level);
}
if (op.max_level > get_entry_level()) {
- _graph.set_entry_node({docid, node_ref, op.max_level});
+ _graph.set_entry_node({nodeid, node_ref, op.max_level});
}
}
@@ -480,19 +481,19 @@ HnswIndex::mutual_reconnect(const LinkArrayRef &cluster, uint32_t level)
}
void
-HnswIndex::remove_document(uint32_t docid)
+HnswIndex::remove_node(uint32_t nodeid)
{
- bool need_new_entrypoint = (docid == get_entry_docid());
- LevelArrayRef node_levels = _graph.get_level_array(docid);
+ bool need_new_entrypoint = (nodeid == get_entry_nodeid());
+ LevelArrayRef node_levels = _graph.get_level_array(nodeid);
for (int level = node_levels.size(); level-- > 0; ) {
- LinkArrayRef my_links = _graph.get_link_array(docid, level);
+ LinkArrayRef my_links = _graph.get_link_array(nodeid, level);
for (uint32_t neighbor_id : my_links) {
if (need_new_entrypoint) {
auto entry_node_ref = _graph.get_node_ref(neighbor_id);
_graph.set_entry_node({neighbor_id, entry_node_ref, level});
need_new_entrypoint = false;
}
- remove_link_to(neighbor_id, docid, level);
+ remove_link_to(neighbor_id, nodeid, level);
}
mutual_reconnect(my_links, level);
}
@@ -500,7 +501,14 @@ HnswIndex::remove_document(uint32_t docid)
HnswGraph::EntryNode entry;
_graph.set_entry_node(entry);
}
- _graph.remove_node_for_document(docid);
+ _graph.remove_node(nodeid);
+}
+
+void
+HnswIndex::remove_document(uint32_t docid)
+{
+ auto nodeid = get_nodeid(docid);
+ remove_node(nodeid);
}
void
@@ -525,8 +533,8 @@ void
HnswIndex::compact_level_arrays(CompactionSpec compaction_spec, const CompactionStrategy& compaction_strategy)
{
auto context = _graph.nodes.compactWorst(compaction_spec, compaction_strategy);
- uint32_t doc_id_limit = _graph.node_refs.size();
- vespalib::ArrayRef<AtomicEntryRef> refs(&_graph.node_refs[0], doc_id_limit);
+ uint32_t nodeid_limit = _graph.node_refs.size();
+ vespalib::ArrayRef<AtomicEntryRef> refs(&_graph.node_refs[0], nodeid_limit);
context->compact(refs);
}
@@ -534,9 +542,9 @@ void
HnswIndex::compact_link_arrays(CompactionSpec compaction_spec, const CompactionStrategy& compaction_strategy)
{
auto context = _graph.links.compactWorst(compaction_spec, compaction_strategy);
- uint32_t doc_id_limit = _graph.node_refs.size();
- for (uint32_t doc_id = 1; doc_id < doc_id_limit; ++doc_id) {
- EntryRef level_ref = _graph.get_node_ref(doc_id);
+ uint32_t nodeid_limit = _graph.node_refs.size();
+ for (uint32_t nodeid = 1; nodeid < nodeid_limit; ++nodeid) {
+ EntryRef level_ref = _graph.get_node_ref(nodeid);
if (level_ref.valid()) {
vespalib::ArrayRef<AtomicEntryRef> refs(_graph.nodes.get_writable(level_ref));
context->compact(refs);
@@ -640,7 +648,7 @@ HnswIndex::get_state(const vespalib::slime::Inserter& inserter) const
object.setLong("unreachable_nodes_incomplete_count", unreachable);
}
auto entry_node = _graph.get_entry_node();
- object.setLong("entry_docid", entry_node.docid);
+ object.setLong("entry_nodeid", entry_node.nodeid);
object.setLong("entry_level", entry_node.level);
auto& cfgObj = object.setObject("cfg");
cfgObj.setLong("max_links_at_level_0", _cfg.max_links_at_level_0());
@@ -670,7 +678,7 @@ HnswIndex::make_saver() const
std::unique_ptr<NearestNeighborIndexLoader>
HnswIndex::make_loader(FastOS_FileInterface& file)
{
- assert(get_entry_docid() == 0); // cannot load after index has data
+ assert(get_entry_nodeid() == 0); // cannot load after index has data
using ReaderType = FileReader<uint32_t>;
using LoaderType = HnswIndexLoader<ReaderType>;
return std::make_unique<LoaderType>(_graph, std::make_unique<ReaderType>(&file));
@@ -697,7 +705,7 @@ HnswIndex::top_k_by_docid(uint32_t k, TypedCells vector,
result.reserve(candidates.size());
for (const HnswCandidate & hit : candidates.peek()) {
if (hit.distance > distance_threshold) continue;
- result.emplace_back(hit.docid, hit.distance);
+ result.emplace_back(get_docid(hit.nodeid), hit.distance);
}
std::sort(result.begin(), result.end(), NeighborsByDocId());
return result;
@@ -723,14 +731,14 @@ HnswIndex::top_k_candidates(const TypedCells &vector, uint32_t k, const GlobalFi
{
FurthestPriQ best_neighbors;
auto entry = _graph.get_entry_node();
- if (entry.docid == 0) {
+ if (entry.nodeid == 0) {
// graph has no entry point
return best_neighbors;
}
int search_level = entry.level;
- double entry_dist = calc_distance(vector, entry.docid);
+ double entry_dist = calc_distance(vector, entry.nodeid);
// TODO: check if entry docid/node_ref is still valid here
- HnswCandidate entry_point(entry.docid, entry.node_ref, entry_dist);
+ HnswCandidate entry_point(entry.nodeid, entry.node_ref, entry_dist);
while (search_level > 0) {
entry_point = find_nearest_in_layer(vector, entry_point, search_level);
--search_level;
@@ -741,9 +749,9 @@ HnswIndex::top_k_candidates(const TypedCells &vector, uint32_t k, const GlobalFi
}
HnswNode
-HnswIndex::get_node(uint32_t docid) const
+HnswIndex::get_node(uint32_t nodeid) const
{
- auto node_ref = _graph.acquire_node_ref(docid);
+ auto node_ref = _graph.acquire_node_ref(nodeid);
if (!node_ref.valid()) {
return HnswNode();
}
@@ -759,17 +767,17 @@ HnswIndex::get_node(uint32_t docid) const
}
void
-HnswIndex::set_node(uint32_t docid, const HnswNode &node)
+HnswIndex::set_node(uint32_t nodeid, const HnswNode &node)
{
size_t num_levels = node.size();
assert(num_levels > 0);
- auto node_ref = _graph.make_node_for_document(docid, num_levels);
+ auto node_ref = _graph.make_node(nodeid, num_levels);
for (size_t level = 0; level < num_levels; ++level) {
- connect_new_node(docid, node.level(level), level);
+ connect_new_node(nodeid, node.level(level), level);
}
int max_level = num_levels - 1;
if (get_entry_level() < max_level) {
- _graph.set_entry_node({docid, node_ref, max_level});
+ _graph.set_entry_node({nodeid, node_ref, max_level});
}
}
@@ -777,20 +785,20 @@ bool
HnswIndex::check_link_symmetry() const
{
bool all_sym = true;
- size_t doc_id_limit = _graph.size();
- for (size_t docid = 0; docid < doc_id_limit; ++docid) {
- auto node_ref = _graph.acquire_node_ref(docid);
+ size_t nodeid_limit = _graph.size();
+ for (size_t nodeid = 0; nodeid < nodeid_limit; ++nodeid) {
+ auto node_ref = _graph.acquire_node_ref(nodeid);
if (node_ref.valid()) {
auto levels = _graph.nodes.get(node_ref);
uint32_t level = 0;
for (const auto& links_ref : levels) {
auto links = _graph.links.get(links_ref.load_acquire());
- for (auto neighbor_docid : links) {
- auto neighbor_links = _graph.acquire_link_array(neighbor_docid, level);
- if (! has_link_to(neighbor_links, docid)) {
+ for (auto neighbor_nodeid : links) {
+ auto neighbor_links = _graph.acquire_link_array(neighbor_nodeid, level);
+ if (! has_link_to(neighbor_links, nodeid)) {
all_sym = false;
- LOG(warning, "check_link_symmetry: docid %zu links to %u on level %u, but no backlink",
- docid, neighbor_docid, level);
+ LOG(warning, "check_link_symmetry: nodeid %zu links to %u on level %u, but no backlink",
+ nodeid, neighbor_nodeid, level);
}
}
++level;
@@ -810,9 +818,9 @@ HnswIndex::count_reachable_nodes() const
}
std::vector<bool> visited(_graph.size());
LinkArray found_links;
- if (entry.docid < visited.size()) {
- found_links.push_back(entry.docid);
- visited[entry.docid] = true;
+ if (entry.nodeid < visited.size()) {
+ found_links.push_back(entry.nodeid);
+ visited[entry.nodeid] = true;
}
vespalib::steady_time doom = vespalib::steady_clock::now() + MAX_COUNT_DURATION;
while (search_level >= 0) {
@@ -820,9 +828,9 @@ HnswIndex::count_reachable_nodes() const
if (vespalib::steady_clock::now() > doom) {
return {found_links.size(), false};
}
- uint32_t docid = found_links[idx];
- if (docid < visited.size()) {
- auto neighbors = _graph.acquire_link_array(docid, search_level);
+ uint32_t nodeid = found_links[idx];
+ if (nodeid < visited.size()) {
+ auto neighbors = _graph.acquire_link_array(nodeid, search_level);
for (uint32_t neighbor : neighbors) {
if (neighbor >= visited.size() || visited[neighbor]) {
continue;
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index.h b/searchlib/src/vespa/searchlib/tensor/hnsw_index.h
index 43e3001b496..1833eec3909 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_index.h
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index.h
@@ -80,6 +80,9 @@ public:
CompactionSpec link_arrays() const noexcept { return _link_arrays; }
};
+ // Stubs for mapping between docid and nodeid.
+ static uint32_t get_docid(uint32_t nodeid) { return nodeid; }
+ static uint32_t get_nodeid(uint32_t docid) { return docid; }
protected:
using AtomicEntryRef = HnswGraph::AtomicEntryRef;
using NodeStore = HnswGraph::NodeStore;
@@ -100,10 +103,10 @@ protected:
HnswIndexCompactionSpec _compaction_spec;
uint32_t max_links_for_level(uint32_t level) const;
- void add_link_to(uint32_t docid, uint32_t level, const LinkArrayRef& old_links, uint32_t new_link) {
+ void add_link_to(uint32_t nodeid, uint32_t level, const LinkArrayRef& old_links, uint32_t new_link) {
LinkArray new_links(old_links.begin(), old_links.end());
new_links.push_back(new_link);
- _graph.set_link_array(docid, level, new_links);
+ _graph.set_link_array(nodeid, level, new_links);
}
/**
@@ -122,18 +125,22 @@ protected:
SelectResult select_neighbors_heuristic(const HnswCandidateVector& neighbors, uint32_t max_links) const;
SelectResult select_neighbors_simple(const HnswCandidateVector& neighbors, uint32_t max_links) const;
SelectResult select_neighbors(const HnswCandidateVector& neighbors, uint32_t max_links) const;
- void shrink_if_needed(uint32_t docid, uint32_t level);
- void connect_new_node(uint32_t docid, const LinkArrayRef &neighbors, uint32_t level);
+ void shrink_if_needed(uint32_t nodeid, uint32_t level);
+ void connect_new_node(uint32_t nodeid, const LinkArrayRef &neighbors, uint32_t level);
void mutual_reconnect(const LinkArrayRef &cluster, uint32_t level);
void remove_link_to(uint32_t remove_from, uint32_t remove_id, uint32_t level);
- inline TypedCells get_vector(uint32_t docid) const {
+ inline TypedCells get_vector(uint32_t nodeid) const {
+ uint32_t docid = get_docid(nodeid);
+ return _vectors.get_vector(docid);
+ }
+ inline TypedCells get_vector_by_docid(uint32_t docid) const {
return _vectors.get_vector(docid);
}
- double calc_distance(uint32_t lhs_docid, uint32_t rhs_docid) const;
- double calc_distance(const TypedCells& lhs, uint32_t rhs_docid) const;
- uint32_t estimate_visited_nodes(uint32_t level, uint32_t doc_id_limit, uint32_t neighbors_to_find, const GlobalFilter* filter) const;
+ double calc_distance(uint32_t lhs_nodeid, uint32_t rhs_nodeid) const;
+ double calc_distance(const TypedCells& lhs, uint32_t rhs_nodeid) const;
+ uint32_t estimate_visited_nodes(uint32_t level, uint32_t nodeid_limit, uint32_t neighbors_to_find, const GlobalFilter* filter) const;
/**
* Performs a greedy search in the given layer to find the candidate that is nearest the input vector.
@@ -142,7 +149,7 @@ protected:
template <class VisitedTracker>
void search_layer_helper(const TypedCells& input, uint32_t neighbors_to_find, FurthestPriQ& found_neighbors,
uint32_t level, const GlobalFilter *filter,
- uint32_t doc_id_limit,
+ uint32_t nodeid_limit,
uint32_t estimated_visited_nodes) const;
void search_layer(const TypedCells& input, uint32_t neighbors_to_find, FurthestPriQ& found_neighbors,
uint32_t level, const GlobalFilter *filter = nullptr) const;
@@ -169,7 +176,7 @@ protected:
};
PreparedAddDoc internal_prepare_add(uint32_t docid, TypedCells input_vector,
vespalib::GenerationHandler::Guard read_guard) const;
- LinkArray filter_valid_docids(uint32_t level, const PreparedAddDoc::Links &neighbors, uint32_t me);
+ LinkArray filter_valid_nodeids(uint32_t level, const PreparedAddDoc::Links &neighbors, uint32_t self_nodeid);
void internal_complete_add(uint32_t docid, PreparedAddDoc &op);
public:
HnswIndex(const DocVectorAccess& vectors, DistanceFunction::UP distance_func,
@@ -184,6 +191,7 @@ public:
TypedCells vector,
vespalib::GenerationHandler::Guard read_guard) const override;
void complete_add_document(uint32_t docid, std::unique_ptr<PrepareResult> prepare_result) override;
+ void remove_node(uint32_t nodeid);
void remove_document(uint32_t docid) override;
void assign_generation(generation_t current_gen) override;
void reclaim_memory(generation_t oldest_used_gen) override;
@@ -210,12 +218,12 @@ public:
FurthestPriQ top_k_candidates(const TypedCells &vector, uint32_t k, const GlobalFilter *filter) const;
- uint32_t get_entry_docid() const { return _graph.get_entry_node().docid; }
+ uint32_t get_entry_nodeid() const { return _graph.get_entry_node().nodeid; }
int32_t get_entry_level() const { return _graph.get_entry_node().level; }
// Should only be used by unit tests.
- HnswNode get_node(uint32_t docid) const;
- void set_node(uint32_t docid, const HnswNode &node);
+ HnswNode get_node(uint32_t nodeid) const;
+ void set_node(uint32_t nodeid, const HnswNode &node);
bool check_link_symmetry() const;
std::pair<uint32_t, bool> count_reachable_nodes() const;
HnswGraph& get_graph() { return _graph; }
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.h b/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.h
index b65ae22ed47..5d40893f215 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.h
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.h
@@ -22,10 +22,10 @@ class HnswIndexLoader : public NearestNeighborIndexLoader {
private:
HnswGraph& _graph;
std::unique_ptr<ReaderType> _reader;
- uint32_t _entry_docid;
+ uint32_t _entry_nodeid;
int32_t _entry_level;
uint32_t _num_nodes;
- uint32_t _docid;
+ uint32_t _nodeid;
std::vector<uint32_t> _link_array;
bool _complete;
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.hpp b/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.hpp
index 4fd75bb2fec..1c38e7d7936 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.hpp
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index_loader.hpp
@@ -13,7 +13,7 @@ template <typename ReaderType>
void
HnswIndexLoader<ReaderType>::init()
{
- _entry_docid = next_int();
+ _entry_nodeid = next_int();
_entry_level = next_int();
_num_nodes = next_int();
}
@@ -25,10 +25,10 @@ template <typename ReaderType>
HnswIndexLoader<ReaderType>::HnswIndexLoader(HnswGraph& graph, std::unique_ptr<ReaderType> reader)
: _graph(graph),
_reader(std::move(reader)),
- _entry_docid(0),
+ _entry_nodeid(0),
_entry_level(0),
_num_nodes(0),
- _docid(0),
+ _nodeid(0),
_link_array(),
_complete(false)
{
@@ -40,28 +40,28 @@ bool
HnswIndexLoader<ReaderType>::load_next()
{
assert(!_complete);
- if (_docid < _num_nodes) {
+ if (_nodeid < _num_nodes) {
uint32_t num_levels = next_int();
if (num_levels > 0) {
- _graph.make_node_for_document(_docid, num_levels);
+ _graph.make_node(_nodeid, num_levels);
for (uint32_t level = 0; level < num_levels; ++level) {
uint32_t num_links = next_int();
_link_array.clear();
while (num_links-- > 0) {
_link_array.push_back(next_int());
}
- _graph.set_link_array(_docid, level, _link_array);
+ _graph.set_link_array(_nodeid, level, _link_array);
}
}
}
- if (++_docid < _num_nodes) {
+ if (++_nodeid < _num_nodes) {
return true;
} else {
_graph.node_refs.ensure_size(std::max(_num_nodes, 1u));
_graph.node_refs_size.store(std::max(_num_nodes, 1u), std::memory_order_release);
_graph.trim_node_refs_size();
- auto entry_node_ref = _graph.get_node_ref(_entry_docid);
- _graph.set_entry_node({_entry_docid, entry_node_ref, _entry_level});
+ auto entry_node_ref = _graph.get_node_ref(_entry_nodeid);
+ _graph.set_entry_node({_entry_nodeid, entry_node_ref, _entry_level});
_complete = true;
return false;
}
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.cpp b/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.cpp
index 29218b47f53..2784dd5af28 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.cpp
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.cpp
@@ -26,7 +26,7 @@ count_valid_link_arrays(const HnswGraph & graph) {
}
HnswIndexSaver::MetaData::MetaData()
- : entry_docid(0),
+ : entry_nodeid(0),
entry_level(-1),
refs(),
nodes()
@@ -38,7 +38,7 @@ HnswIndexSaver::HnswIndexSaver(const HnswGraph &graph)
: _graph_links(graph.links), _meta_data()
{
auto entry = graph.get_entry_node();
- _meta_data.entry_docid = entry.docid;
+ _meta_data.entry_nodeid = entry.nodeid;
_meta_data.entry_level = entry.level;
size_t num_nodes = graph.node_refs.get_size(); // Called from writer only
assert (num_nodes <= (std::numeric_limits<uint32_t>::max() - 1));
@@ -62,7 +62,7 @@ HnswIndexSaver::HnswIndexSaver(const HnswGraph &graph)
void
HnswIndexSaver::save(BufferWriter& writer) const
{
- writer.write(&_meta_data.entry_docid, sizeof(uint32_t));
+ writer.write(&_meta_data.entry_nodeid, sizeof(uint32_t));
writer.write(&_meta_data.entry_level, sizeof(int32_t));
uint32_t num_nodes = _meta_data.nodes.size() - 1;
writer.write(&num_nodes, sizeof(uint32_t));
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.h b/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.h
index e170a4a894f..11a1f993c4e 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.h
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index_saver.h
@@ -25,7 +25,7 @@ public:
private:
struct MetaData {
using EntryRef = vespalib::datastore::EntryRef;
- uint32_t entry_docid;
+ uint32_t entry_nodeid;
int32_t entry_level;
std::vector<EntryRef, vespalib::allocator_large<EntryRef>> refs;
std::vector<uint32_t, vespalib::allocator_large<uint32_t>> nodes;
diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index_utils.h b/searchlib/src/vespa/searchlib/tensor/hnsw_index_utils.h
index abb7ecc199a..66255c3f797 100644
--- a/searchlib/src/vespa/searchlib/tensor/hnsw_index_utils.h
+++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index_utils.h
@@ -13,13 +13,13 @@ namespace search::tensor {
* Represents a candidate node with its distance to another point in space.
*/
struct HnswCandidate {
- uint32_t docid;
+ uint32_t nodeid;
HnswGraph::NodeRef node_ref;
double distance;
- HnswCandidate(uint32_t docid_in, double distance_in) noexcept
- : docid(docid_in), node_ref(), distance(distance_in) {}
- HnswCandidate(uint32_t docid_in, HnswGraph::NodeRef node_ref_in, double distance_in) noexcept
- : docid(docid_in), node_ref(node_ref_in), distance(distance_in) {}
+ HnswCandidate(uint32_t nodeid_in, double distance_in) noexcept
+ : nodeid(nodeid_in), node_ref(), distance(distance_in) {}
+ HnswCandidate(uint32_t nodeid_in, HnswGraph::NodeRef node_ref_in, double distance_in) noexcept
+ : nodeid(nodeid_in), node_ref(node_ref_in), distance(distance_in) {}
};
struct GreaterDistance {
diff --git a/vespalog/src/logger/runserver.cpp b/vespalog/src/logger/runserver.cpp
index f9dde87ca14..7cec2be6f3f 100644
--- a/vespalog/src/logger/runserver.cpp
+++ b/vespalog/src/logger/runserver.cpp
@@ -342,6 +342,7 @@ int usage(char *prog, int es)
int main(int argc, char *argv[])
{
bool doStop = false;
+ bool checkWouldRun = false;
int restart = 0;
const char *service = "runserver";
const char *pidfile = "vespa-runserver.pid"; // XXX bad default?
@@ -350,7 +351,7 @@ int main(int argc, char *argv[])
signal(SIGQUIT, SIG_IGN);
int ch;
- while ((ch = getopt(argc, argv, "k:s:r:p:Sh")) != -1) {
+ while ((ch = getopt(argc, argv, "k:s:r:p:ShW")) != -1) {
switch (ch) {
case 's':
service = optarg;
@@ -367,6 +368,9 @@ int main(int argc, char *argv[])
case 'k':
killcmd = optarg;
break;
+ case 'W':
+ checkWouldRun = true;
+ break;
default:
return usage(argv[0], ch != 'h');
}
@@ -383,6 +387,14 @@ int main(int argc, char *argv[])
}
PidFile mypf(pidfile);
+ if (checkWouldRun) {
+ if (mypf.anotherRunning()) {
+ fprintf(stderr, "%s already running with pid %d\n", service, mypf.readPid());
+ return 1;
+ } else {
+ return 0;
+ }
+ }
if (doStop) {
if (mypf.anotherRunning()) {
int pid = mypf.readPid();