diff options
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> @@ -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(); |