Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/126401.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 126401
summary: Add pinned retriever
area: Relevance
type: enhancement
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ static TransportVersion def(int id) {
public static final TransportVersion SEARCH_INCREMENTAL_TOP_DOCS_NULL_BACKPORT_8_19 = def(8_841_0_20);
public static final TransportVersion ML_INFERENCE_SAGEMAKER_8_19 = def(8_841_0_21);
public static final TransportVersion ESQL_REPORT_ORIGINAL_TYPES_BACKPORT_8_19 = def(8_841_0_22);
public static final TransportVersion PINNED_RETRIEVER_8_19 = def(8_841_0_23);

/*
* STOP! READ THIS FIRST! No, really,
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugin/ent-search/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ esplugin {
name = 'x-pack-ent-search'
description = 'Elasticsearch Expanded Pack Plugin - Enterprise Search'
classname = 'org.elasticsearch.xpack.application.EnterpriseSearch'
extendedPlugins = ['x-pack-core']
extendedPlugins = ['x-pack-core', 'search-business-rules']
}

base {
Expand All @@ -17,7 +17,7 @@ base {

dependencies {
compileOnly project(path: xpackModule('core'))
implementation project(xpackModule('search-business-rules'))
compileOnly project(xpackModule('search-business-rules'))
api project(':modules:lang-mustache')

// JSON Schema dependencies
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugin/rank-rrf/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies {
clusterModules project(xpackModule('rank-rrf'))
clusterModules project(xpackModule('inference'))
clusterModules project(':modules:lang-painless')
clusterModules project(xpackModule('search-business-rules'))

clusterPlugins project(':x-pack:plugin:inference:qa:test-service-plugin')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
setup:
- requires:
cluster_features: 'pinned_retriever_supported'
reason: 'test requires pinned retriever implementation'
- do:
indices.create:
index: test-index1

- do:
bulk:
refresh: true
index: test-index1
body:
- index:
_id: doc1
- { "text": "document one" }
- index:
_id: doc2
- { "text": "document two" }
- index:
_id: doc3
- { "text": "document three" }
- index:
_id: doc4
- { "text": "document four" }
- index:
_id: doc5
- { "text": "document five" }

---
"rrf combined with pinned retriever":
- skip: { features: headers }
- do:
headers:
Content-Type: application/json
search:
index: test-index1
body:
retriever:
pinned:
ids: ["doc1"]
retriever:
rrf:
retrievers: [
{
standard: {
query: {
term: { text: "document" }
}
}
},
{
standard: {
query: {
term: { text: "three" }
}
}
}
]
rank_window_size: 10

- match: { hits.total.value: 5 }
- match: { hits.hits.0._id: doc1 }
- match: { hits.hits.0._score: 1.7014122E38 }
- match: { hits.hits.1._id: doc3 }
- match: { hits.hits.1._score < 100.0 }
- match: { hits.hits.2._id: doc2 }

---
"rrf with pinned retriever as a sub-retriever":
- skip: { features: headers }
- do:
headers: { Content-Type: application/json }
search:
index: test-index1
body:
retriever:
rrf:
retrievers:
-
standard:
query:
match: { text: "document" }
-
pinned:
ids: ["doc4", "doc5"]
retriever:
standard:
query:
match: { text: "document" }

- match: { hits.total.value: 5 }
- match: { hits.hits.0._id: doc1 }
- lt: { hits.hits.0._score: 100.0 }
- match: { hits.hits.1._id: doc4 }
- match: { hits.hits.2._id: doc5 }


16 changes: 15 additions & 1 deletion x-pack/plugin/search-business-rules/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
apply plugin: 'elasticsearch.internal-es-plugin'
apply plugin: 'elasticsearch.internal-cluster-test'
apply plugin: 'elasticsearch.internal-yaml-rest-test'

esplugin {
name = 'search-business-rules'
Expand All @@ -13,6 +14,19 @@ base {

dependencies {
compileOnly project(path: xpackModule('core'))
compileOnly project(':server')
testImplementation(testArtifact(project(xpackModule('core'))))
testImplementation project(":test:framework")
testImplementation(testArtifact(project(':server')))
clusterModules project(xpackModule('search-business-rules'))
clusterModules project(':modules:mapper-extras')
clusterModules project(':modules:lang-painless')
clusterModules project(xpackModule('inference'))
}

tasks.named("yamlRestTest") {
usesDefaultDistribution("uses search business rules plugin")
}
artifacts {
restXpackTests(new File(projectDir, "src/yamlRestTest/resources/rest-api-spec/test"))
}

Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@
requires org.elasticsearch.xcore;

exports org.elasticsearch.xpack.searchbusinessrules;

provides org.elasticsearch.features.FeatureSpecification with org.elasticsearch.xpack.searchbusinessrules.SearchBusinessRulesFeatures;
provides org.elasticsearch.plugins.SearchPlugin with org.elasticsearch.xpack.searchbusinessrules.SearchBusinessRules;
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public class PinnedQueryBuilder extends AbstractQueryBuilder<PinnedQueryBuilder>

// Organic queries will have their scores capped to this number range,
// We reserve the highest float exponent for scores of pinned queries
private static final float MAX_ORGANIC_SCORE = Float.intBitsToFloat((0xfe << 23)) - 1;
public static final float MAX_ORGANIC_SCORE = Float.intBitsToFloat((0xfe << 23)) - 1;

public PinnedQueryBuilder(QueryBuilder organicQuery, String... ids) {
this(organicQuery, Arrays.asList(ids), null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,28 @@

package org.elasticsearch.xpack.searchbusinessrules;

import org.elasticsearch.plugins.ExtensiblePlugin;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.plugins.SearchPlugin;
import org.elasticsearch.plugins.SearchPlugin.QuerySpec;
import org.elasticsearch.plugins.SearchPlugin.RetrieverSpec;
import org.elasticsearch.xcontent.ParseField;
import org.elasticsearch.xpack.searchbusinessrules.retriever.PinnedRetrieverBuilder;

import java.util.List;

import static java.util.Collections.singletonList;

public class SearchBusinessRules extends Plugin implements SearchPlugin {
public class SearchBusinessRules extends Plugin implements SearchPlugin, ExtensiblePlugin {

@Override
public List<QuerySpec<?>> getQueries() {
return singletonList(new QuerySpec<>(PinnedQueryBuilder.NAME, PinnedQueryBuilder::new, PinnedQueryBuilder::fromXContent));
return List.of(new QuerySpec<>(PinnedQueryBuilder.NAME, PinnedQueryBuilder::new, PinnedQueryBuilder::fromXContent));
}

@Override
public List<RetrieverSpec<?>> getRetrievers() {
return singletonList(new RetrieverSpec<>(new ParseField(PinnedRetrieverBuilder.NAME), PinnedRetrieverBuilder::fromXContent));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.searchbusinessrules;

import org.elasticsearch.features.FeatureSpecification;
import org.elasticsearch.features.NodeFeature;

import java.util.Set;

import static org.elasticsearch.xpack.searchbusinessrules.retriever.PinnedRetrieverBuilder.PINNED_RETRIEVER_FEATURE;

public class SearchBusinessRulesFeatures implements FeatureSpecification {

@Override
public Set<NodeFeature> getFeatures() {
return Set.of();
}

@Override
public Set<NodeFeature> getTestFeatures() {
return Set.of(PINNED_RETRIEVER_FEATURE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
return builder.endObject();
}

static final ConstructingObjectParser<SpecifiedDocument, Void> PARSER = new ConstructingObjectParser<>(
public static final ConstructingObjectParser<SpecifiedDocument, Void> PARSER = new ConstructingObjectParser<>(
NAME,
a -> new SpecifiedDocument((String) a[0], (String) a[1])
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.searchbusinessrules.retriever;

import org.apache.lucene.search.Explanation;
import org.elasticsearch.TransportVersion;
import org.elasticsearch.TransportVersions;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.search.rank.RankDoc;

import java.io.IOException;
import java.util.Objects;

public class PinnedRankDoc extends RankDoc {
public static final String NAME = "pinned_rank_doc";

private final boolean isPinned;

public PinnedRankDoc(int docId, float score, int shardIndex, boolean isPinned) {
super(docId, score, shardIndex);
this.isPinned = isPinned;
}

public PinnedRankDoc(StreamInput in) throws IOException {
super(in);
this.isPinned = in.readBoolean();
}

public boolean isPinned() {
return isPinned;
}

@Override
public Explanation explain(Explanation[] sources, String[] queryNames) {
if (isPinned) {
return Explanation.match(score, "Pinned document, original explanation:", sources);
} else {
return super.explain(sources, queryNames);
}
}

@Override
public String toString() {
return super.toString() + ", isPinned=" + isPinned;
}

@Override
public String getWriteableName() {
return NAME;
}

@Override
protected void doWriteTo(StreamOutput out) throws IOException {
out.writeBoolean(isPinned);
}

@Override
protected boolean doEquals(RankDoc rd) {
if (rd instanceof PinnedRankDoc other) {
return this.isPinned == other.isPinned;
} else {
return false;
}
}

@Override
protected int doHashCode() {
return Objects.hash(super.doHashCode(), isPinned);
}

@Override
public TransportVersion getMinimalSupportedVersion() {
return TransportVersions.PINNED_RETRIEVER_8_19;
}
}
Loading