All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.appengine.api.search.Query Maven / Gradle / Ivy

/*
 * Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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 com.google.appengine.api.search;

import com.google.appengine.api.search.checkers.QueryChecker;
import com.google.appengine.api.search.proto.SearchServicePb.SearchParams;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * A query to search an index for documents which match,
 * restricting the document fields returned to those given, and
 * scoring and sorting the results, whilst supporting pagination.
 * 

* For example, the following query will search for documents where * the tokens 'good' and 'story' occur in some fields, * returns up to 20 results including the fields 'author' and 'date-sent' * as well as snippeted fields 'subject' and 'body'. The results * are sorted by 'author' in descending order, getting the next 20 results * from the responseCursor in the previously returned results, giving * back a single cursor in the {@link Results} to get the next * batch of results after this. *

*

{@code
 * QueryOptions options = QueryOptions.newBuilder()
 *     .setLimit(20)
 *     .setFieldsToSnippet("subject", "body")
 *     .setScorer(CustomScorer.newBuilder()
 *         .addSortExpression(SortExpression.newBuilder()
 *             .setExpression("author")
 *             .setDirection(SortDirection.DESCENDING)
 *             .setDefaultValue("")))
 *     .setCursor(responseCursor)
 *     .build();
 * Query query = Query.newBuilder()
 *     .setOptions(options)
 *     .build("good story");
 * }
 * 
* * The following query will return facet information with the query result: *
 * Query query = Query.newBuilder()
 *     .setOptions(options)
 *     .setEnableFacetDiscovery(true)
 *     .build("tablet");
 * 
* * To customize returned facet or refine the result using a previously returned * {@link FacetResultValue#getRefinementToken}: *
 * Query query = Query.newBuilder()
 *     .setOptions(options)
 *     .setEnableFacetDiscovery(true)
 *     .setFacetOptions(FacetOptions.newBuilder().setDiscoveryLimit(5).build())
 *     .addReturnFacet("shipping")
 *     .addReturnFacet(FacetRequest.newBuilder().setName("department")
 *         .addValueConstraint("Computers")
 *         .addValueConstraint("Electronics")
 *         .build())
 *     .addRefinementToken(refinementToken1)
 *     .addRefinementToken(refinementToken2)
 *     .build("tablet");
 * 
*/ public class Query { /** * A builder which constructs Query objects. */ public static class Builder { // Mandatory private String queryString; private boolean enableFacetDiscovery; // Optional @Nullable private QueryOptions options; @Nullable private FacetOptions facetOptions; @Nullable private List returnFacets = new ArrayList<>(); @Nullable private List refinements = new ArrayList<>(); // NOTE: We will add query id and schema as Nullable attributes. protected Builder() {} /** * Constructs a {@link Query} builder with the given query. * * @param query the query to populate the builder */ private Builder(Query query) { this.queryString = query.getQueryString(); this.enableFacetDiscovery = query.getEnableFacetDiscovery(); this.options = query.getOptions(); this.facetOptions = query.getFacetOptions(); this.returnFacets = new ArrayList<>(query.getReturnFacets()); this.refinements = new ArrayList<>(query.getRefinements()); } /** * Sets the query options. * * @param options the {@link QueryOptions} to apply to the search results * @return this builder */ public Builder setOptions(QueryOptions options) { this.options = options; return this; } /** * Sets the query options from a builder. * * @param optionsBuilder the {@link QueryOptions.Builder} build a * {@link QueryOptions} to apply to the search results * @return this builder */ public Builder setOptions(QueryOptions.Builder optionsBuilder) { return setOptions(optionsBuilder.build()); } /** * Sets the facet options. * * @param options the {@link FacetOptions} to apply to the facet results * @return this builder */ public Builder setFacetOptions(FacetOptions options) { this.facetOptions = options; return this; } /** * Sets the facet options from a builder. * * @param builder the {@link FacetOptions.Builder} build a {@link FacetOptions} * to apply to the facet results * @return this builder */ public Builder setFacetOptions(FacetOptions.Builder builder) { return this.setFacetOptions(builder.build()); } /** * Sets enable facet discovery flag. * * @return this builder */ public Builder setEnableFacetDiscovery(boolean value) { this.enableFacetDiscovery = value; return this; } /** * Requests a facet to be returned with search results. The facet will be included in * the result regardless of the number of values it has. * * @param facet the {@link FacetRequest} to be added to return facets. * @return this builder */ public Builder addReturnFacet(FacetRequest facet) { this.returnFacets.add(facet); return this; } /** * Adds a facet refinement token. The token is returned by each FacetResultValue. There will be * disjunction between tokens for the same facet and conjunction between tokens for different * facets. For example if the refinement tokens are (name=wine_type,value=red), * (name=wine_type,value=white) and (name=year, Range(2000,2010)), * the result will be refined according to: *
     * ((wine_type is red) OR (wine_type is white)) AND (year in Range(2000,2010))
     * 
* * @param token the token returned by {@link FacetResultValue#getRefinementToken} or * {@link FacetRefinement#toTokenString}. * @return this builder * @throws IllegalArgumentException if token is not valid. */ public Builder addFacetRefinementFromToken(String token) { return addFacetRefinement(FacetRefinement.fromTokenString(token)); } /** * Adds a facet refinement. There will be disjunction between refinements for the same facet * and conjunction between refinements for different facets. For example if the refinements are * (name=wine_type,value=red), (name=wine_type,value=white) and (name=year, Range(2000,2010)), * the result will be refined according to: *
     * ((wine_type is red) OR (wine_type is white)) AND (year in Range.closedOpen(2000,2010))
     * 
* * @param refinement a {@link FacetRefinement} object. * @return this builder */ public Builder addFacetRefinement(FacetRefinement refinement) { this.refinements.add(refinement); return this; } /** * Adds a facet request from a builder. * * @param builder the {@link FacetRequest.Builder} build a {@link FacetRequest} * to be added to return facets. * @return this builder */ public Builder addReturnFacet(FacetRequest.Builder builder) { return addReturnFacet(builder.build()); } /** * Adds a facet request by its name only. * * @param facetName the name of the facet to be added to return facets. * @return this builder */ public Builder addReturnFacet(String facetName) { this.returnFacets.add(FacetRequest.newBuilder().setName(facetName).build()); return this; } /** * Sets the query string used to construct the query. * * @param query a query string used to construct the query * @return this Builder * @throws SearchQueryException if the query string does not parse */ protected Builder setQueryString(String query) { this.queryString = QueryChecker.checkQuery(query); return this; } /** * Build a {@link Query} from the query string and the parameters set on * the {@link Builder}. A query string can be as simple as a single term * ("foo"), or as complex as a boolean expression, including field names * ("title:hello OR body:important -october"). * * @param queryString the query string to parse and apply to an index * @return the Query built from the parameters entered on this * Builder including the queryString * @throws SearchQueryException if the query string is invalid */ public Query build(String queryString) { // TODO: put in url to publically available documentation about // VanillaST in the javadoc. setQueryString(queryString); return new Query(this); } /** * Construct the message. * * @return the Query built from the parameters entered on this * Builder * @throws IllegalArgumentException if the query string is invalid */ public Query build() { return new Query(this); } } // Mandatory private final String query; private final boolean enableFacetDiscovery; // Optional @Nullable private final QueryOptions options; @Nullable private final FacetOptions facetOptions; @Nullable private final ImmutableList returnFacets; @Nullable private final ImmutableList refinements; /** * Creates a query from the builder. * * @param builder the query builder to populate with */ protected Query(Builder builder) { query = builder.queryString; enableFacetDiscovery = builder.enableFacetDiscovery; options = builder.options; facetOptions = builder.facetOptions; returnFacets = ImmutableList.copyOf(builder.returnFacets); refinements = ImmutableList.copyOf(builder.refinements); checkValid(); } /** * The query can be as simple as a single term ("foo"), or as complex * as a boolean expression, including field names ("title:hello OR * body:important -october"). * * @return the query */ public String getQueryString() { return query; } /** * Returns the {@link QueryOptions} for controlling the what is returned * in the result set matching the query */ public QueryOptions getOptions() { return options; } /** * Returns the {@link FacetOptions} for controlling faceted search or null if unset. */ public FacetOptions getFacetOptions() { return facetOptions; } /** * Returns true if facet discovery is enabled. */ public boolean getEnableFacetDiscovery() { return enableFacetDiscovery; } /** * Returns an unmodifiable list of requests for facets to be returned with the search results. */ public ImmutableList getReturnFacets() { return returnFacets; } /** * Returns an unmodifiable list of facet refinements for the search. */ public ImmutableList getRefinements() { return refinements; } /** * Creates and returns a {@link Query} builder. Set the query * parameters and use the {@link Builder#build()} method to create a concrete * instance of Query. * * @return a {@link Builder} which can construct a query */ public static Builder newBuilder() { return new Builder(); } /** * Creates a builder from the given query. * * @param query the query for the builder to use to build another query * @return a new builder with values based on the given request */ public static Builder newBuilder(Query query) { return new Builder(query); } /** * Checks the query is valid, specifically, has a non-null * query string. * * @return this checked Query * @throws NullPointerException if query is null */ private Query checkValid() { Preconditions.checkNotNull(query, "query cannot be null"); return this; } /** * Copies the contents of this {@link Query} object into a * {@link SearchParams} protocol buffer builder. * * @return a search params protocol buffer builder initialized with * the values from this query * @throws IllegalArgumentException if the cursor type is * unknown */ SearchParams.Builder copyToProtocolBuffer() { SearchParams.Builder builder = SearchParams.newBuilder().setQuery(query); if (options == null) { // Use all the default settings from QueryOptions. QueryOptions.newBuilder().build().copyToProtocolBuffer(builder, query); } else { options.copyToProtocolBuffer(builder, query); } if (facetOptions == null) { FacetOptions.newBuilder().build().copyToProtocolBuffer(builder, enableFacetDiscovery); } else { facetOptions.copyToProtocolBuffer(builder, enableFacetDiscovery); } for (FacetRequest facet : returnFacets) { builder.addIncludeFacet(facet.copyToProtocolBuffer()); } for (FacetRefinement ref : refinements) { builder.addFacetRefinement(ref.toProtocolBuffer()); } return builder; } @Override public String toString() { return new Util.ToStringHelper("Query") .addField("queryString", query) .addField("options", options) .addField("enableFacetDiscovery", enableFacetDiscovery ? Boolean.TRUE : null) .addField("facetOptions", facetOptions) .addIterableField("returnFacets", returnFacets) .addIterableField("refinements", refinements) .finish(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy