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

main.java.com.cloudant.client.api.Search Maven / Gradle / Ivy

There is a newer version: 2.20.1
Show newest version
/*
 * Copyright © 2015, 2019 IBM Corp. All rights reserved.
 *
 * 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 com.cloudant.client.api;

import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.jsonToObject;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.assertNotEmpty;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.close;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.getAsLong;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.getAsString;

import com.cloudant.client.api.model.SearchResult;
import com.cloudant.client.internal.DatabaseURIHelper;
import com.cloudant.http.Http;
import com.cloudant.http.HttpConnection;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;

/**
 * This class provides access to the Cloudant Search APIs.
 * 

Note: The design document name and search index name are required. * Do not include the {@code _design} prefix from the {@code _id} of the design document. *

Usage Example:

*
 * {@code
 *  // Search query using design document _id '_design/views101' and search index 'animals'
 *  List birds = db.search("views101/animals")
 * 	.limit(10)
 * 	.includeDocs(true)
 * 	.query("class:bird", Bird.class);
 *
 * // groups
 * Map> birdGroups = db.search("views101/animals")
 * 	.limit(10)
 * 	.includeDocs(true)
 * 	.queryGroups("class:bird", Bird.class);
 * for ( Entry> group : birdGroups.entrySet()) {
 * 		System.out.println("Group Name : " +  group.getKey());
 * 		for ( Bird b : group.getValue() ) {
 * 			 System.out.println("\t" + b);
 * 		 }
 * 	}
 *
 *  // search results object
 *  SearchResult result = db.search("views101/animals")
 *   .querySearchResult("class:bird", Bird.class);
 *
 *  // pagination
 *  SearchResult nextPage = db.search("views101/animals")
 *   .bookmark(result.bookmark)
 *   .querySearchResult("class:bird", Bird.class);
 * }
 * 
* * @author Mario Briggs * @see Database#search(String) * @see SearchResult * @since 0.0.1 */ public class Search { private static final Logger log = Logger.getLogger(Search.class.getCanonicalName()); // search fields private Integer limit; private boolean includeDocs = false; private String bookmark; private CloudantClient client; private DatabaseURIHelper databaseHelper; private final String partitionKey; Search(CloudantClient client, Database db, String partitionKey, String searchIndexId) { assertNotEmpty(searchIndexId, "searchIndexId"); this.client = client; this.partitionKey = partitionKey; String search = searchIndexId; this.databaseHelper = new DatabaseURIHelper(db.getDBUri()).partition(partitionKey); if (searchIndexId.contains("/")) { String[] v = searchIndexId.split("/"); this.databaseHelper.path("_design").path(v[0]).path("_search").path(v[1]); } else { this.databaseHelper.path(search); } } // Query options /** * Performs a Cloudant Search and returns the result as an {@link InputStream} * * @param query the Lucene query to be passed to the Search index *

The stream should be properly closed after usage, as to avoid connection * leaks. * @return The result as an {@link InputStream}. */ public InputStream queryForStream(String query) { key(query); URI uri = databaseHelper.build(); HttpConnection get = Http.GET(uri); get.requestProperties.put("Accept", "application/json"); return client.couchDbClient.executeToInputStream(get); } /** * Queries a Search Index and returns ungrouped results. In case the query * used grouping, an empty list is returned * * @param Object type T * @param query the Lucene query to be passed to the Search index * @param classOfT The class of type T * @return The result of the search query as a {@code List } */ public List query(String query, Class classOfT) { InputStream instream = null; List result = new ArrayList(); try { Reader reader = new InputStreamReader(instream = queryForStream(query), "UTF-8"); JsonObject json = new JsonParser().parse(reader).getAsJsonObject(); if (json.has("rows")) { if (!includeDocs) { log.warning("includeDocs set to false and attempting to retrieve doc. " + "null object will be returned"); } for (JsonElement e : json.getAsJsonArray("rows")) { result.add(jsonToObject(client.getGson(), e, "doc", classOfT)); } } else { log.warning("No ungrouped result available. Use queryGroups() if grouping set"); } return result; } catch (UnsupportedEncodingException e1) { // This should never happen as every implementation of the java platform is required // to support UTF-8. throw new RuntimeException(e1); } finally { close(instream); } } /** * Queries a Search Index and returns grouped results in a map where key * of the map is the groupName. In case the query didnt use grouping, * an empty map is returned * * @param Object type T * @param query the Lucene query to be passed to the Search index * @param classOfT The class of type T * @return The result of the grouped search query as a ordered {@code Map } */ public Map> queryGroups(String query, Class classOfT) { InputStream instream = null; try { Reader reader = new InputStreamReader(instream = queryForStream(query), "UTF-8"); JsonObject json = new JsonParser().parse(reader).getAsJsonObject(); Map> result = new LinkedHashMap>(); if (json.has("groups")) { for (JsonElement e : json.getAsJsonArray("groups")) { String groupName = e.getAsJsonObject().get("by").getAsString(); List orows = new ArrayList(); if (!includeDocs) { log.warning("includeDocs set to false and attempting to retrieve doc. " + "null object will be returned"); } for (JsonElement rows : e.getAsJsonObject().getAsJsonArray("rows")) { orows.add(jsonToObject(client.getGson(), rows, "doc", classOfT)); } result.put(groupName, orows); }// end for(groups) }// end hasgroups else { log.warning("No grouped results available. Use query() if non grouped query"); } return result; } catch (UnsupportedEncodingException e1) { // This should never happen as every implementation of the java platform is required // to support UTF-8. throw new RuntimeException(e1); } finally { close(instream); } } /** * Performs a Cloudant Search and returns the result as an {@link SearchResult} * * @param Object type T, an instance into which the rows[].doc/group[].rows[].doc * attribute of the Search result response should be deserialized into. Same * goes for the rows[].fields/group[].rows[].fields attribute * @param query the Lucene query to be passed to the Search index * @param classOfT The class of type T. * @return The Search result entries */ public SearchResult querySearchResult(String query, Class classOfT) { InputStream instream = null; try { Reader reader = new InputStreamReader(instream = queryForStream(query), "UTF-8"); JsonObject json = new JsonParser().parse(reader).getAsJsonObject(); SearchResult sr = new SearchResult(); sr.setTotalRows(getAsLong(json, "total_rows")); sr.setBookmark(getAsString(json, "bookmark")); if (json.has("rows")) { sr.setRows(getRows(json.getAsJsonArray("rows"), sr, classOfT)); } else if (json.has("groups")) { setGroups(json.getAsJsonArray("groups"), sr, classOfT); } if (json.has("counts")) { sr.setCounts(getFieldsCounts(json.getAsJsonObject("counts").entrySet())); } if (json.has("ranges")) { sr.setRanges(getFieldsCounts(json.getAsJsonObject("ranges").entrySet())); } return sr; } catch (UnsupportedEncodingException e) { // This should never happen as every implementation of the java platform is required // to support UTF-8. throw new RuntimeException(e); } finally { close(instream); } } /** * @param limit limit the number of documents in the result * @return this for additional parameter setting or to query */ public Search limit(Integer limit) { this.limit = limit; databaseHelper.query("limit", this.limit); return this; } /** * Control which page of results to get. The bookmark value is obtained by executing * the query()/queryForStream() once and getting it from the bookmark field * in the response * * @param bookmark see the next page of results after this bookmark result * @return this for additional parameter setting or to query */ public Search bookmark(String bookmark) { this.bookmark = bookmark; databaseHelper.query("bookmark", this.bookmark); return this; } /** * Specify the sort order for the result. * * @param sortJson JSON string specifying the sort order * @return this for additional parameter setting or to query * @see * Search query syntax */ public Search sort(String sortJson) { assertNotEmpty(sortJson, "sort"); databaseHelper.query("sort", sortJson); return this; } /** * Group results by the specified field. * * @param fieldName by which to group results * @param isNumber whether field isNumeric. * @return this for additional parameter setting or to query */ public Search groupField(String fieldName, boolean isNumber) { assertNotEmpty(fieldName, "fieldName"); if (isNumber) { databaseHelper.query("group_field", fieldName + ""); } else { databaseHelper.query("group_field", fieldName); } return this; } /** * Maximum group count when groupField is set * * @param limit the maximum group count * @return this for additional parameter setting or to query */ public Search groupLimit(int limit) { databaseHelper.query("group_limit", limit); return this; } /** * the sort order of the groups when groupField is set * * @param groupsortJson JSON string specifying the group sort * @return this for additional parameter setting or to query * @see * Search query syntax */ public Search groupSort(String groupsortJson) { assertNotEmpty(groupsortJson, "groupsortJson"); databaseHelper.query("group_sort", groupsortJson); return this; } /** * Ranges for faceted searches * * @param rangesJson JSON string specifying the ranges * @return this for additional parameter setting or to query * @see * Search query syntax */ public Search ranges(String rangesJson) { assertNotEmpty(rangesJson, "rangesJson"); databaseHelper.query("ranges", rangesJson); return this; } /** * Array of fieldNames for which counts should be produced * * @param countsfields array of the field names * @return this for additional parameter setting or to query */ public Search counts(String[] countsfields) { assert (countsfields.length > 0); JsonArray countsJsonArray = new JsonArray(); for(String countsfield : countsfields) { JsonPrimitive element = new JsonPrimitive(countsfield); countsJsonArray.add(element); } databaseHelper.query("counts", countsJsonArray); return this; } /** * @param fieldName the name of the field * @param fieldValue the value of the field * @return this for additional parameter setting or to query * @see * drilldown query parameter * @deprecated Use {@link #drillDown(String, String...)} */ @Deprecated public Search drillDown(String fieldName, String fieldValue) { assertNotEmpty(fieldName, "fieldName"); assertNotEmpty(fieldValue, "fieldValue"); JsonArray drillDownArray = new JsonArray(); JsonPrimitive fieldNamePrimitive = new JsonPrimitive(fieldName); drillDownArray.add(fieldNamePrimitive); JsonPrimitive fieldValuePrimitive = new JsonPrimitive(fieldValue); drillDownArray.add(fieldValuePrimitive); databaseHelper.query("drilldown", drillDownArray, false); return this; } /** * @param fieldName the name of the field * @param fieldValues field values to add * @return this for additional parameter setting or to query * @see drilldown query parameter */ public Search drillDown(String fieldName, String... fieldValues) { assertNotEmpty(fieldName, "fieldName"); assertNotEmpty(fieldValues, "fieldValues"); JsonArray drillDownArray = new JsonArray(); JsonPrimitive fieldNamePrimitive = new JsonPrimitive(fieldName); drillDownArray.add(fieldNamePrimitive); for (String fieldValue : fieldValues) { JsonPrimitive fieldValuePrimitive = new JsonPrimitive(fieldValue); drillDownArray.add(fieldValuePrimitive); } databaseHelper.query("drilldown", drillDownArray, false); return this; } /** * @param stale Accept values: ok * @return this for additional parameter setting or to query */ public Search stale(boolean stale) { if (stale) { databaseHelper.query("stale", "ok"); } return this; } /** * @param includeDocs whether to include the document in the result * @return this for additional parameter setting or to query */ public Search includeDocs(Boolean includeDocs) { this.includeDocs = includeDocs; databaseHelper.query("include_docs", this.includeDocs); return this; } private void key(String query) { databaseHelper.query("q", query); } private Map> getFieldsCounts(Set> fldset) { Map> map = new HashMap>(); for (Entry fld : fldset) { String field = fld.getKey(); Map ovalues = new HashMap(); if (fld.getValue().isJsonObject()) { Set> values = fld.getValue().getAsJsonObject() .entrySet(); for (Entry value : values) { ovalues.put(value.getKey(), value.getValue().getAsLong()); } } map.put(field, ovalues); } return map; } private List.SearchResultRow> getRows( JsonArray jsonrows, SearchResult sr, Class classOfT) { List.SearchResultRow> ret = new ArrayList .SearchResultRow>(); for (JsonElement e : jsonrows) { SearchResult.SearchResultRow row = sr.new SearchResultRow(); JsonObject oe = e.getAsJsonObject(); row.setId(oe.get("id").getAsString()); row.setOrder(jsonToObject(client.getGson(), e, "order", Object[].class)); row.setFields(jsonToObject(client.getGson(), e, "fields", classOfT)); if (includeDocs) { row.setDoc(jsonToObject(client.getGson(), e, "doc", classOfT)); } ret.add(row); } return ret; } private void setGroups(JsonArray jsongroups, SearchResult sr, Class classOfT) { for (JsonElement e : jsongroups) { SearchResult.SearchResultGroup group = sr.new SearchResultGroup(); JsonObject oe = e.getAsJsonObject(); group.setBy(oe.get("by").getAsString()); group.setTotalRows(oe.get("total_rows").getAsLong()); group.setRows(getRows(oe.getAsJsonArray("rows"), sr, classOfT)); sr.getGroups().add(group); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy