/* * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.appsearch; import static android.app.appsearch.SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE; import static android.app.appsearch.SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_EUCLIDEAN; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.appsearch.annotation.CanIgnoreReturnValue; import android.app.appsearch.safeparcel.AbstractSafeParcelable; import android.app.appsearch.safeparcel.GenericDocumentParcel; import android.app.appsearch.safeparcel.SafeParcelable; import android.app.appsearch.util.BundleUtil; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; import com.android.appsearch.flags.Flags; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** * This class represents one of the results obtained from an AppSearch query. * *
This allows clients to obtain: * *
"Snippet" refers to a substring of text from the content of document that is returned as a
* part of search result.
*
* @see SearchResults
*/
@SafeParcelable.Class(creator = "SearchResultCreator")
// TODO(b/384721898): Switch to JSpecify annotations
@SuppressWarnings({"HiddenSuperclass", "JSpecifyNullness"})
public final class SearchResult extends AbstractSafeParcelable {
@FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
public static final @NonNull Parcelable.Creator The map includes entries for the {@link GenericDocument}'s own type and all of the nested
* documents' types. Child types are guaranteed to appear before parent types in each list.
*
* Parent types include transitive parents.
*
* All schema names in this map are un-prefixed, for both keys and values.
*/
@Field(id = 8)
final @NonNull Bundle mParentTypeMap;
/** Cache of the {@link GenericDocument}. Comes from mDocument at first use. */
private @Nullable GenericDocument mDocumentCached;
/** Cache of the inflated {@link MatchInfo}. Comes from inflating mMatchInfos at first use. */
private @Nullable List The meaning of the ranking signal and its value is determined by the selected ranking
* strategy:
*
* The map includes entries for the {@link GenericDocument}'s own type and all of the nested
* documents' types. Child types are guaranteed to appear before parent types in each list.
*
* Parent types include transitive parents.
*
* Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this
* function, rather than calling it multiple times.
*/
@FlaggedApi(Flags.FLAG_ENABLE_SEARCH_RESULT_PARENT_TYPES)
public @NonNull Map These joined documents match the outer document as specified in the {@link JoinSpec} with
* parentPropertyExpression and childPropertyExpression. They are ordered according to the
* {@link JoinSpec#getNestedSearchSpec}, and as many SearchResults as specified by {@link
* JoinSpec#getMaxJoinedResultCount} will be returned. If no {@link JoinSpec} was specified,
* this returns an empty list.
*
* This method is inefficient to call repeatedly, as new {@link SearchResult} objects are
* created each time.
*
* @return a List of SearchResults containing joined documents.
*/
public @NonNull List The map should include entries for the {@link GenericDocument}'s own type and all of
* the nested documents' types.
*
* Child types must appear before parent types in each list. Otherwise, the
* GenericDocument's toDocumentClass method (an AndroidX-only API) may not correctly
* identify the most concrete type. This could lead to unintended deserialization into a
* more general type instead of a more specific type.
*
* Parent types should include transitive parents.
*/
@CanIgnoreReturnValue
@FlaggedApi(Flags.FLAG_ENABLE_SEARCH_RESULT_PARENT_TYPES)
public @NonNull Builder setParentTypeMap(@NonNull Map A {@link MatchInfo} contains either a {@link TextMatchInfo} representing a text match
* snippet, or an {@link EmbeddingMatchInfo} representing an embedding match snippet.
*/
@SafeParcelable.Class(creator = "MatchInfoCreator")
@SuppressWarnings("HiddenSuperclass")
public static final class MatchInfo extends AbstractSafeParcelable {
@FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
public static final @NonNull Parcelable.Creator If this is {@code null}, methods which require access to the document, like {@link
* #getExactMatch}, will throw {@link NullPointerException}.
*/
private @Nullable GenericDocument mDocument = null;
@Constructor
MatchInfo(
@Param(id = 1) @NonNull String propertyPath,
@Param(id = 2) int exactMatchRangeStart,
@Param(id = 3) int exactMatchRangeEnd,
@Param(id = 4) int submatchRangeStart,
@Param(id = 5) int submatchRangeEnd,
@Param(id = 6) int snippetRangeStart,
@Param(id = 7) int snippetRangeEnd,
@Param(id = 8) @Nullable TextMatchInfo textMatchInfo,
@Param(id = 9) @Nullable EmbeddingMatchInfo embeddingMatchInfo) {
mPropertyPath = Objects.requireNonNull(propertyPath);
mExactMatchRangeStart = exactMatchRangeStart;
mExactMatchRangeEnd = exactMatchRangeEnd;
mSubmatchRangeStart = submatchRangeStart;
mSubmatchRangeEnd = submatchRangeEnd;
mSnippetRangeStart = snippetRangeStart;
mSnippetRangeEnd = snippetRangeEnd;
mEmbeddingMatch = embeddingMatchInfo;
TextMatchInfo tempTextMatch = textMatchInfo;
if (tempTextMatch == null && mEmbeddingMatch == null) {
tempTextMatch =
new TextMatchInfo(
exactMatchRangeStart, exactMatchRangeEnd,
submatchRangeStart, submatchRangeEnd,
snippetRangeStart, snippetRangeEnd);
tempTextMatch.setPropertyPath(mPropertyPath);
}
mTextMatch = tempTextMatch;
}
/**
* Gets the property path corresponding to the given entry.
*
* A property path is a '.' - delimited sequence of property names indicating which
* property in the document these snippets correspond to.
*
* Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class
* example 1 this returns "subject"
*/
public @NonNull String getPropertyPath() {
return mPropertyPath;
}
/**
* Gets a {@link PropertyPath} object representing the property path corresponding to the
* given entry.
*
* Methods such as {@link GenericDocument#getPropertyDocument} accept a path as a string
* rather than a {@link PropertyPath} object. However, you may want to manipulate the path
* before getting a property document. This method returns a {@link PropertyPath} rather
* than a String for easier path manipulation, which can then be converted to a String.
*
* @see #getPropertyPath
* @see PropertyPath
*/
public @NonNull PropertyPath getPropertyPathObject() {
if (mPropertyPathObject == null) {
mPropertyPathObject = new PropertyPath(mPropertyPath);
}
return mPropertyPathObject;
}
/**
* Retrieves the text-based match information.
*
* @return A {@link TextMatchInfo} instance, or null if the match is not text-based.
*/
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public @Nullable TextMatchInfo getTextMatch() {
return mTextMatch;
}
/**
* Retrieves the embedding-based match information. Only populated when {@link
* SearchSpec#shouldRetrieveEmbeddingMatchInfos()} is true.
*
* @return A {@link EmbeddingMatchInfo} instance, or null if the match is not an embedding
* match.
*/
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public @Nullable EmbeddingMatchInfo getEmbeddingMatch() {
return mEmbeddingMatch;
}
/**
* Gets the full text corresponding to the given entry. Returns an empty string if the match
* is not text-based.
*/
public @NonNull String getFullText() {
if (mTextMatch == null) {
return "";
}
return mTextMatch.getFullText();
}
/**
* Gets the {@link MatchRange} of the exact term of the given entry that matched the query.
* Returns [0, 0] if the match is not text-based.
*/
public @NonNull MatchRange getExactMatchRange() {
if (mTextMatch == null) {
return new MatchRange(0, 0);
}
return mTextMatch.getExactMatchRange();
}
/**
* Gets the exact term of the given entry that matched the query. Returns an empty
* CharSequence if the match is not text-based.
*/
public @NonNull CharSequence getExactMatch() {
if (mTextMatch == null) {
return "";
}
return mTextMatch.getExactMatch();
}
/**
* Gets the {@link MatchRange} of the submatch term subsequence of the given entry that
* matched the query. Returns [0, 0] if the match is not text-based.
*/
public @NonNull MatchRange getSubmatchRange() {
if (mTextMatch == null) {
return new MatchRange(0, 0);
}
return mTextMatch.getSubmatchRange();
}
/**
* Gets the exact term subsequence of the given entry that matched the query. Returns an
* empty CharSequence if the match is not text-based.
*/
public @NonNull CharSequence getSubmatch() {
if (mTextMatch == null) {
return "";
}
return mTextMatch.getSubmatch();
}
/**
* Gets the snippet {@link MatchRange} corresponding to the given entry. Returns [0,0] if
* the match is not text-based.
*
* Only populated when set maxSnippetSize > 0 in {@link
* SearchSpec.Builder#setMaxSnippetSize}.
*/
public @NonNull MatchRange getSnippetRange() {
if (mTextMatch == null) {
return new MatchRange(0, 0);
}
return mTextMatch.getSnippetRange();
}
/**
* Gets the snippet corresponding to the given entry. Returns an empty CharSequence if the
* match is not text-based.
*
* Snippet - Provides a subset of the content to display. Only populated when requested
* maxSnippetSize > 0. The size of this content can be changed by {@link
* SearchSpec.Builder#setMaxSnippetSize}. Windowing is centered around the middle of the
* matched token with content on either side clipped to token boundaries.
*/
public @NonNull CharSequence getSnippet() {
if (mTextMatch == null) {
return "";
}
return mTextMatch.getSnippet();
}
/**
* Sets the {@link GenericDocument} for {@link MatchInfo}.
*
* {@link MatchInfo} lacks a constructor that populates {@link MatchInfo#mDocument} This
* provides the ability to set {@link MatchInfo#mDocument}
*/
void setDocument(@NonNull GenericDocument document) {
mDocument = Objects.requireNonNull(document);
if (mTextMatch != null) {
mTextMatch.setDocument(document);
}
}
@FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
MatchInfoCreator.writeToParcel(this, dest, flags);
}
/** Builder for {@link MatchInfo} objects. */
public static final class Builder {
private final String mPropertyPath;
private EmbeddingMatchInfo mEmbeddingMatch = null;
private MatchRange mExactMatchRange = new MatchRange(0, 0);
private MatchRange mSubmatchRange = new MatchRange(-1, -1);
private MatchRange mSnippetRange = new MatchRange(0, 0);
/**
* Creates a new {@link MatchInfo.Builder} reporting a match with the given property
* path.
*
* A property path is a dot-delimited sequence of property names indicating which
* property in the document these snippets correspond to.
*
* Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class
* example 1, this returns "subject".
*
* @param propertyPath A dot-delimited sequence of property names indicating which
* property in the document these snippets correspond to.
*/
public Builder(@NonNull String propertyPath) {
mPropertyPath = Objects.requireNonNull(propertyPath);
}
/** @hide */
public Builder(@NonNull MatchInfo matchInfo) {
Objects.requireNonNull(matchInfo);
mPropertyPath = matchInfo.mPropertyPath;
mEmbeddingMatch = matchInfo.getEmbeddingMatch();
mExactMatchRange = matchInfo.getExactMatchRange();
// Using the fields directly instead of getSubmatchRange() to bypass the
// checkSubmatchSupported check.
mSubmatchRange =
new MatchRange(matchInfo.mSubmatchRangeStart, matchInfo.mSubmatchRangeEnd);
mSnippetRange = matchInfo.getSnippetRange();
}
/** Sets the {@link EmbeddingMatchInfo} corresponding to the given entry. */
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
@CanIgnoreReturnValue
public @NonNull Builder setEmbeddingMatch(@Nullable EmbeddingMatchInfo embeddingMatch) {
mEmbeddingMatch = embeddingMatch;
return this;
}
/** Sets the exact {@link MatchRange} corresponding to the given entry. */
@CanIgnoreReturnValue
public @NonNull Builder setExactMatchRange(@NonNull MatchRange matchRange) {
mExactMatchRange = Objects.requireNonNull(matchRange);
return this;
}
/** Sets the submatch {@link MatchRange} corresponding to the given entry. */
@CanIgnoreReturnValue
public @NonNull Builder setSubmatchRange(@NonNull MatchRange matchRange) {
mSubmatchRange = Objects.requireNonNull(matchRange);
return this;
}
/** Sets the snippet {@link MatchRange} corresponding to the given entry. */
@CanIgnoreReturnValue
public @NonNull Builder setSnippetRange(@NonNull MatchRange matchRange) {
mSnippetRange = Objects.requireNonNull(matchRange);
return this;
}
/** Constructs a new {@link MatchInfo}. */
public @NonNull MatchInfo build() {
TextMatchInfo textMatch = null;
if (mEmbeddingMatch == null) {
textMatch =
new TextMatchInfo(
mExactMatchRange.getStart(), mExactMatchRange.getEnd(),
mSubmatchRange.getStart(), mSubmatchRange.getEnd(),
mSnippetRange.getStart(), mSnippetRange.getEnd());
textMatch.setPropertyPath(mPropertyPath);
}
return new MatchInfo(
mPropertyPath,
mExactMatchRange.getStart(),
mExactMatchRange.getEnd(),
mSubmatchRange.getStart(),
mSubmatchRange.getEnd(),
mSnippetRange.getStart(),
mSnippetRange.getEnd(),
textMatch,
mEmbeddingMatch);
}
}
}
/**
* This class represents match objects for any text match snippets that might be present in
* {@link SearchResults} from a string query. Using this class, you can get:
*
* Class Example 1:
*
* A document contains the following text in property "subject":
*
* "A commonly used fake word is foo. Another nonsense word that’s used a lot is bar."
*
* If the queryExpression is "foo" and {@link SearchSpec#getMaxSnippetSize} is 10,
*
*
*
* Class Example 2:
*
* A document contains one property named "subject" and one property named "sender" which
* contains a "name" property.
*
* In this case, we will have 2 property paths: {@code sender.name} and {@code subject}.
*
* Let {@code sender.name = "Test Name Jr."} and {@code subject = "Testing 1 2 3"}
*
* If the queryExpression is "Test" with {@link SearchSpec#TERM_MATCH_PREFIX} and {@link
* SearchSpec#getMaxSnippetSize} is 10. We will have 2 matches:
*
* Match-1
*
* Match-2
*
* If this is {@code null}, methods which require access to the property, like {@link
* #getExactMatch}, will throw {@link NullPointerException}.
*/
private @Nullable String mPropertyPath = null;
/**
* Document which the match comes from.
*
* If this is {@code null}, methods which require access to the document, like {@link
* #getExactMatch}, will throw {@link NullPointerException}.
*/
private @Nullable GenericDocument mDocument = null;
/** Full text of the matched property. Populated on first use. */
private @Nullable String mFullText;
/** Range of property that exactly matched the query. Populated on first use. */
private @Nullable MatchRange mExactMatchRangeCached;
/**
* Range of property that corresponds to the subsequence of the exact match that directly
* matches a query term. Populated on first use.
*/
private @Nullable MatchRange mSubmatchRangeCached;
/** Range of some reasonable amount of context around the query. Populated on first use. */
private @Nullable MatchRange mWindowRangeCached;
/**
* Creates a new immutable TextMatchInfo.
*
* @param exactMatchRangeStart the start of the exact {@link MatchRange} for the entry.
* @param exactMatchRangeEnd the end of the exact {@link MatchRange} for the entry.
* @param submatchRangeStart the start of the sub-match {@link MatchRange} for the entry.
* @param submatchRangeEnd the end of the sub-match {@link MatchRange} for the entry.
* @param snippetRangeStart the start of the snippet {@link MatchRange} for the entry.
* @param snippetRangeEnd the end of the snippet {@link MatchRange} for the entry.
*/
@Constructor
public TextMatchInfo(
@Param(id = 1) int exactMatchRangeStart,
@Param(id = 2) int exactMatchRangeEnd,
@Param(id = 3) int submatchRangeStart,
@Param(id = 4) int submatchRangeEnd,
@Param(id = 5) int snippetRangeStart,
@Param(id = 6) int snippetRangeEnd) {
mExactMatchRangeStart = exactMatchRangeStart;
mExactMatchRangeEnd = exactMatchRangeEnd;
mSubmatchRangeStart = submatchRangeStart;
mSubmatchRangeEnd = submatchRangeEnd;
mSnippetRangeStart = snippetRangeStart;
mSnippetRangeEnd = snippetRangeEnd;
}
/**
* Gets the full text corresponding to the given entry.
*
* Class example 1: this returns "A commonly used fake word is foo. Another nonsense word
* that's used a lot is bar."
*
* Class example 2: for the first {@link TextMatchInfo}, this returns "Test Name Jr."
* and, for the second {@link TextMatchInfo}, this returns "Testing 1 2 3".
*/
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public @NonNull String getFullText() {
if (mFullText == null) {
if (mDocument == null || mPropertyPath == null) {
throw new IllegalStateException(
"Document or property path has not been populated; this TextMatchInfo"
+ " cannot be used yet");
}
mFullText = getPropertyValues(mDocument, mPropertyPath);
}
return mFullText;
}
/**
* Gets the {@link MatchRange} of the exact term of the given entry that matched the query.
*
* Class example 1: this returns [29, 32].
*
* Class example 2: for the first {@link TextMatchInfo}, this returns [0, 4] and, for the
* second {@link TextMatchInfo}, this returns [0, 7].
*/
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public @NonNull MatchRange getExactMatchRange() {
if (mExactMatchRangeCached == null) {
mExactMatchRangeCached = new MatchRange(mExactMatchRangeStart, mExactMatchRangeEnd);
}
return mExactMatchRangeCached;
}
/**
* Gets the exact term of the given entry that matched the query.
*
* Class example 1: this returns "foo".
*
* Class example 2: for the first {@link TextMatchInfo}, this returns "Test" and, for the
* second {@link TextMatchInfo}, this returns "Testing".
*/
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public @NonNull CharSequence getExactMatch() {
return getSubstring(getExactMatchRange());
}
/**
* Gets the {@link MatchRange} of the exact term subsequence of the given entry that matched
* the query.
*
* Class example 1: this returns [29, 32].
*
* Class example 2: for the first {@link TextMatchInfo}, this returns [0, 4] and, for the
* second {@link TextMatchInfo}, this returns [0, 4].
*/
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public @NonNull MatchRange getSubmatchRange() {
checkSubmatchSupported();
if (mSubmatchRangeCached == null) {
mSubmatchRangeCached = new MatchRange(mSubmatchRangeStart, mSubmatchRangeEnd);
}
return mSubmatchRangeCached;
}
/**
* Gets the exact term subsequence of the given entry that matched the query.
*
* Class example 1: this returns "foo".
*
* Class example 2: for the first {@link TextMatchInfo}, this returns "Test" and, for the
* second {@link TextMatchInfo}, this returns "Test".
*/
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public @NonNull CharSequence getSubmatch() {
checkSubmatchSupported();
return getSubstring(getSubmatchRange());
}
/**
* Gets the snippet {@link TextMatchInfo} corresponding to the given entry.
*
* Only populated when set maxSnippetSize > 0 in {@link
* SearchSpec.Builder#setMaxSnippetSize}.
*
* Class example 1: this returns [29, 41].
*
* Class example 2: for the first {@link TextMatchInfo}, this returns [0, 9] and, for the
* second {@link TextMatchInfo}, this returns [0, 13].
*/
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public @NonNull MatchRange getSnippetRange() {
if (mWindowRangeCached == null) {
mWindowRangeCached = new MatchRange(mSnippetRangeStart, mSnippetRangeEnd);
}
return mWindowRangeCached;
}
/**
* Gets the snippet corresponding to the given entry.
*
* Snippet - Provides a subset of the content to display. Only populated when requested
* maxSnippetSize > 0. The size of this content can be changed by {@link
* SearchSpec.Builder#setMaxSnippetSize}. Windowing is centered around the middle of the
* matched token with content on either side clipped to token boundaries.
*
* Class example 1: this returns "foo. Another".
*
* Class example 2: for the first {@link TextMatchInfo}, this returns "Test Name" and,
* for the second {@link TextMatchInfo}, this returns "Testing 1 2 3".
*/
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public @NonNull CharSequence getSnippet() {
return getSubstring(getSnippetRange());
}
private CharSequence getSubstring(MatchRange range) {
return getFullText().substring(range.getStart(), range.getEnd());
}
private void checkSubmatchSupported() {
if (mSubmatchRangeStart == -1) {
throw new UnsupportedOperationException(
"Submatch is not supported with this backend/Android API level "
+ "combination");
}
}
/** Extracts the matching string from the document. */
private static String getPropertyValues(GenericDocument document, String propertyName) {
String result = document.getPropertyString(propertyName);
if (result == null) {
throw new IllegalStateException(
"No content found for requested property path: " + propertyName);
}
return result;
}
/**
* Sets the {@link GenericDocument} for this {@link TextMatchInfo}.
*
* {@link TextMatchInfo} lacks a constructor that populates {@link
* TextMatchInfo#mDocument} This provides the ability to set {@link TextMatchInfo#mDocument}
*/
void setDocument(@NonNull GenericDocument document) {
mDocument = Objects.requireNonNull(document);
}
/**
* Sets the property path for this {@link TextMatchInfo}.
*
* {@link TextMatchInfo} lacks a constructor that populates {@link
* TextMatchInfo#mPropertyPath} This provides the ability to set it.
*/
void setPropertyPath(@NonNull String propertyPath) {
mPropertyPath = Objects.requireNonNull(propertyPath);
}
@FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
TextMatchInfoCreator.writeToParcel(this, dest, flags);
}
}
/**
* This class represents match objects for any snippets that might be present in {@link
* SearchResults} from an embedding query. Using this class, you can get:
*
* All ranges are finite, and the left side of the range is always {@code <=} the right side
* of the range.
*
* Example: MatchRange(0, 100) represents hundred ints from 0 to 99."
*/
public static final class MatchRange {
private final int mEnd;
private final int mStart;
/**
* Creates a new immutable range.
*
* The endpoints are {@code [start, end)}; that is the range is bounded. {@code start}
* must be lesser or equal to {@code end}.
*
* @param start The start point (inclusive)
* @param end The end point (exclusive)
*/
public MatchRange(int start, int end) {
if (start > end) {
throw new IllegalArgumentException(
"Start point must be less than or equal to " + "end point");
}
mStart = start;
mEnd = end;
}
/** Gets the start point (inclusive). */
public int getStart() {
return mStart;
}
/** Gets the end point (exclusive). */
public int getEnd() {
return mEnd;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof MatchRange)) {
return false;
}
MatchRange otherMatchRange = (MatchRange) other;
return this.getStart() == otherMatchRange.getStart()
&& this.getEnd() == otherMatchRange.getEnd();
}
@Override
public @NonNull String toString() {
return "MatchRange { start: " + mStart + " , end: " + mEnd + "}";
}
@Override
public int hashCode() {
return Objects.hash(mStart, mEnd);
}
}
}
*
*
* @return Ranking signal of the document
*/
public double getRankingSignal() {
return mRankingSignal;
}
/**
* Returns the informational ranking signals of the {@link GenericDocument}, according to the
* expressions added in {@link SearchSpec.Builder#addInformationalRankingExpressions}.
*/
@FlaggedApi(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
public @NonNull List
*
*
* for each text match in the document.
*
*
*
*
*
*
*
*
*
*/
@SafeParcelable.Class(creator = "TextMatchInfoCreator")
@SuppressWarnings("HiddenSuperclass")
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public static final class TextMatchInfo extends AbstractSafeParcelable {
@FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
public static final @NonNull Parcelable.Creator
*
*
* for each vector match in the document.
*/
@SafeParcelable.Class(creator = "EmbeddingMatchInfoCreator")
@SuppressWarnings("HiddenSuperclass")
@FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO)
public static final class EmbeddingMatchInfo extends AbstractSafeParcelable {
@FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
public static final @NonNull Parcelable.Creator