http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrQuery.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrQuery.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrQuery.java new file mode 100644 index 0000000..bda6f34 --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrQuery.java @@ -0,0 +1,1109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj; + +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.FacetParams; +import org.apache.solr.common.params.HighlightParams; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.StatsParams; +import org.apache.solr.common.params.TermsParams; +import org.apache.solr.common.util.DateUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + + +/** + * This is an augmented SolrParams with get/set/add fields for common fields used + * in the Standard and Dismax request handlers + * + * + * @since solr 1.3 + */ +public class SolrQuery extends ModifiableSolrParams +{ + public static final String DOCID = "_docid_"; // duplicate of org.apache.solr.search.QueryParsing.DOCID which is not accessible from here + + public enum ORDER { desc, asc; + public ORDER reverse() { + return (this == asc) ? desc : asc; + } + } + + /** Maintains a map of current sorts */ + private List<SortClause> sortClauses; + + public SolrQuery() { + super(); + } + + /** Create a new SolrQuery + * + * @param q query string + */ + public SolrQuery(String q) { + this(); + this.set(CommonParams.Q, q); + } + + /** enable/disable terms. + * + * @param b flag to indicate terms should be enabled. <br> if b==false, removes all other terms parameters + * @return Current reference (<i>this</i>) + */ + public SolrQuery setTerms(boolean b) { + if (b) { + this.set(TermsParams.TERMS, true); + } else { + this.remove(TermsParams.TERMS); + this.remove(TermsParams.TERMS_FIELD); + this.remove(TermsParams.TERMS_LOWER); + this.remove(TermsParams.TERMS_UPPER); + this.remove(TermsParams.TERMS_UPPER_INCLUSIVE); + this.remove(TermsParams.TERMS_LOWER_INCLUSIVE); + this.remove(TermsParams.TERMS_LIMIT); + this.remove(TermsParams.TERMS_PREFIX_STR); + this.remove(TermsParams.TERMS_MINCOUNT); + this.remove(TermsParams.TERMS_MAXCOUNT); + this.remove(TermsParams.TERMS_RAW); + this.remove(TermsParams.TERMS_SORT); + this.remove(TermsParams.TERMS_REGEXP_STR); + this.remove(TermsParams.TERMS_REGEXP_FLAG); + } + return this; + } + + public boolean getTerms() { + return this.getBool(TermsParams.TERMS, false); + } + + public SolrQuery addTermsField(String field) { + this.add(TermsParams.TERMS_FIELD, field); + return this; + } + + public String[] getTermsFields() { + return this.getParams(TermsParams.TERMS_FIELD); + } + + public SolrQuery setTermsLower(String lower) { + this.set(TermsParams.TERMS_LOWER, lower); + return this; + } + + public String getTermsLower() { + return this.get(TermsParams.TERMS_LOWER, ""); + } + + public SolrQuery setTermsUpper(String upper) { + this.set(TermsParams.TERMS_UPPER, upper); + return this; + } + + public String getTermsUpper() { + return this.get(TermsParams.TERMS_UPPER, ""); + } + + public SolrQuery setTermsUpperInclusive(boolean b) { + this.set(TermsParams.TERMS_UPPER_INCLUSIVE, b); + return this; + } + + public boolean getTermsUpperInclusive() { + return this.getBool(TermsParams.TERMS_UPPER_INCLUSIVE, false); + } + + public SolrQuery setTermsLowerInclusive(boolean b) { + this.set(TermsParams.TERMS_LOWER_INCLUSIVE, b); + return this; + } + + public boolean getTermsLowerInclusive() { + return this.getBool(TermsParams.TERMS_LOWER_INCLUSIVE, true); + } + + public SolrQuery setTermsLimit(int limit) { + this.set(TermsParams.TERMS_LIMIT, limit); + return this; + } + + public int getTermsLimit() { + return this.getInt(TermsParams.TERMS_LIMIT, 10); + } + + public SolrQuery setTermsMinCount(int cnt) { + this.set(TermsParams.TERMS_MINCOUNT, cnt); + return this; + } + + public int getTermsMinCount() { + return this.getInt(TermsParams.TERMS_MINCOUNT, 1); + } + + public SolrQuery setTermsMaxCount(int cnt) { + this.set(TermsParams.TERMS_MAXCOUNT, cnt); + return this; + } + + public int getTermsMaxCount() { + return this.getInt(TermsParams.TERMS_MAXCOUNT, -1); + } + + public SolrQuery setTermsPrefix(String prefix) { + this.set(TermsParams.TERMS_PREFIX_STR, prefix); + return this; + } + + public String getTermsPrefix() { + return this.get(TermsParams.TERMS_PREFIX_STR, ""); + } + + public SolrQuery setTermsRaw(boolean b) { + this.set(TermsParams.TERMS_RAW, b); + return this; + } + + public boolean getTermsRaw() { + return this.getBool(TermsParams.TERMS_RAW, false); + } + + public SolrQuery setTermsSortString(String type) { + this.set(TermsParams.TERMS_SORT, type); + return this; + } + + public String getTermsSortString() { + return this.get(TermsParams.TERMS_SORT, TermsParams.TERMS_SORT_COUNT); + } + + public SolrQuery setTermsRegex(String regex) { + this.set(TermsParams.TERMS_REGEXP_STR, regex); + return this; + } + + public String getTermsRegex() { + return this.get(TermsParams.TERMS_REGEXP_STR); + } + + public SolrQuery setTermsRegexFlag(String flag) { + this.add(TermsParams.TERMS_REGEXP_FLAG, flag); + return this; + } + + public String[] getTermsRegexFlags() { + return this.getParams(TermsParams.TERMS_REGEXP_FLAG); + } + + /** Add field(s) for facet computation. + * + * @param fields Array of field names from the IndexSchema + * @return this + */ + public SolrQuery addFacetField(String ... fields) { + add(FacetParams.FACET_FIELD, fields); + this.set(FacetParams.FACET, true); + return this; + } + + /** Add field(s) for pivot computation. + * + * pivot fields are comma separated + * + * @param fields Array of field names from the IndexSchema + * @return this + */ + public SolrQuery addFacetPivotField(String ... fields) { + add(FacetParams.FACET_PIVOT, fields); + this.set(FacetParams.FACET, true); + return this; + } + + /** + * Add a numeric range facet. + * + * @param field The field + * @param start The start of range + * @param end The end of the range + * @param gap The gap between each count + * @return this + */ + public SolrQuery addNumericRangeFacet(String field, Number start, Number end, Number gap) { + add(FacetParams.FACET_RANGE, field); + add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_START), start.toString()); + add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_END), end.toString()); + add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_GAP), gap.toString()); + this.set(FacetParams.FACET, true); + return this; + } + + /** + * Add a numeric range facet. + * + * @param field The field + * @param start The start of range + * @param end The end of the range + * @param gap The gap between each count + * @return this + */ + public SolrQuery addDateRangeFacet(String field, Date start, Date end, String gap) { + add(FacetParams.FACET_RANGE, field); + add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_START), DateUtil.getThreadLocalDateFormat().format(start)); + add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_END), DateUtil.getThreadLocalDateFormat().format(end)); + add(String.format(Locale.ROOT, "f.%s.%s", field, FacetParams.FACET_RANGE_GAP), gap); + this.set(FacetParams.FACET, true); + return this; + } + + /** + * Add Interval Faceting on a field. All intervals for the same field should be included + * in the same call to this method. + * For syntax documentation see <a href="https://wiki.apache.org/solr/SimpleFacetParameters#Interval_Faceting">Solr wiki</a>. + * <br> + * Key substitution, filter exclusions or other local params on the field are not supported when using this method, + * if this is needed, use the lower level {@link #add} method.<br> + * Key substitution IS supported on intervals when using this method. + * + * + * @param field the field to add facet intervals. Must be an existing field and can't be null + * @param intervals Intervals to be used for faceting. It can be an empty array, but it can't + * be <code>null</code> + * @return this + */ + public SolrQuery addIntervalFacets(String field, String[] intervals) { + if (intervals == null) { + throw new IllegalArgumentException("Can't add null intervals"); + } + if (field == null) { + throw new IllegalArgumentException("Field can't be null"); + } + set(FacetParams.FACET, true); + add(FacetParams.FACET_INTERVAL, field); + for (String interval:intervals) { + add(String.format(Locale.ROOT, "f.%s.facet.interval.set", field), interval); + } + return this; + } + + /** + * Remove all Interval Facets on a field + * + * @param field the field to remove from facet intervals + * @return Array of current intervals for <code>field</code> + */ + public String[] removeIntervalFacets(String field) { + while(remove(FacetParams.FACET_INTERVAL, field)){}; + return remove(String.format(Locale.ROOT, "f.%s.facet.interval.set", field)); + } + + /** get the facet fields + * + * @return string array of facet fields or null if not set/empty + */ + public String[] getFacetFields() { + return this.getParams(FacetParams.FACET_FIELD); + } + + /** remove a facet field + * + * @param name Name of the facet field to be removed. + * + * @return true, if the item was removed. <br> + * false, if the facet field was null or did not exist. + */ + public boolean removeFacetField(String name) { + boolean b = this.remove(FacetParams.FACET_FIELD, name); + if (this.get(FacetParams.FACET_FIELD) == null && this.get(FacetParams.FACET_QUERY) == null) { + this.setFacet(false); + } + return b; + } + + /** enable/disable faceting. + * + * @param b flag to indicate faceting should be enabled. <br> if b==false, removes all other faceting parameters + * @return Current reference (<i>this</i>) + */ + public SolrQuery setFacet(boolean b) { + if (b) { + this.set(FacetParams.FACET, true); + } else { + this.remove(FacetParams.FACET); + this.remove(FacetParams.FACET_MINCOUNT); + this.remove(FacetParams.FACET_FIELD); + this.remove(FacetParams.FACET_LIMIT); + this.remove(FacetParams.FACET_MISSING); + this.remove(FacetParams.FACET_OFFSET); + this.remove(FacetParams.FACET_PREFIX); + this.remove(FacetParams.FACET_QUERY); + this.remove(FacetParams.FACET_SORT); + this.remove(FacetParams.FACET_ZEROS); + this.remove(FacetParams.FACET_PREFIX); // does not include the individual fields... + this.remove(FacetParams.FACET_INTERVAL); // does not remove interval parameters + } + return this; + } + + public SolrQuery setFacetPrefix( String prefix ) + { + this.set( FacetParams.FACET_PREFIX, prefix ); + return this; + } + + public SolrQuery setFacetPrefix( String field, String prefix ) + { + this.set( "f."+field+"."+FacetParams.FACET_PREFIX, prefix ); + return this; + } + + /** add a faceting query + * + * @param f facet query + */ + public SolrQuery addFacetQuery(String f) { + this.add(FacetParams.FACET_QUERY, f); + this.set(FacetParams.FACET, true); + return this; + } + + /** get facet queries + * + * @return all facet queries or null if not set/empty + */ + public String[] getFacetQuery() { + return this.getParams(FacetParams.FACET_QUERY); + } + + /** remove a facet query + * + * @param q the facet query to remove + * @return true if the facet query was removed false otherwise + */ + public boolean removeFacetQuery(String q) { + boolean b = this.remove(FacetParams.FACET_QUERY, q); + if (this.get(FacetParams.FACET_FIELD) == null && this.get(FacetParams.FACET_QUERY) == null) { + this.setFacet(false); + } + return b; + } + + /** set the facet limit + * + * @param lim number facet items to return + */ + public SolrQuery setFacetLimit(int lim) { + this.set(FacetParams.FACET_LIMIT, lim); + return this; + } + + /** get current facet limit + * + * @return facet limit or default of 25 + */ + public int getFacetLimit() { + return this.getInt(FacetParams.FACET_LIMIT, 25); + } + + /** set facet minimum count + * + * @param cnt facets having less that cnt hits will be excluded from teh facet list + */ + public SolrQuery setFacetMinCount(int cnt) { + this.set(FacetParams.FACET_MINCOUNT, cnt); + return this; + } + + /** get facet minimum count + * + * @return facet minimum count or default of 1 + */ + public int getFacetMinCount() { + return this.getInt(FacetParams.FACET_MINCOUNT, 1); + } + + /** + * Sets facet missing boolean flag + * + * @param v flag to indicate the field of {@link FacetParams#FACET_MISSING} . + * @return this + */ + public SolrQuery setFacetMissing(Boolean v) { + this.set(FacetParams.FACET_MISSING, v); + return this; + } + + /** get facet sort + * + * @return facet sort or default of {@link FacetParams#FACET_SORT_COUNT} + */ + public String getFacetSortString() { + return this.get(FacetParams.FACET_SORT, FacetParams.FACET_SORT_COUNT); + } + + + /** set facet sort + * + * @param sort sort facets + * @return this + */ + public SolrQuery setFacetSort(String sort) { + this.set(FacetParams.FACET_SORT, sort); + return this; + } + + /** add highlight field + * + * @param f field to enable for highlighting + */ + public SolrQuery addHighlightField(String f) { + this.add(HighlightParams.FIELDS, f); + this.set(HighlightParams.HIGHLIGHT, true); + return this; + } + + /** remove a field for highlighting + * + * @param f field name to not highlight + * @return <i>true</i>, if removed, <br> <i>false</i>, otherwise + */ + public boolean removeHighlightField(String f) { + boolean b = this.remove(HighlightParams.FIELDS, f); + if (this.get(HighlightParams.FIELDS) == null) { + this.setHighlight(false); + } + return b; + } + + /** get list of highlighted fields + * + * @return Array of highlight fields or null if not set/empty + */ + public String[] getHighlightFields() { + return this.getParams(HighlightParams.FIELDS); + } + + public SolrQuery setHighlightSnippets(int num) { + this.set(HighlightParams.SNIPPETS, num); + return this; + } + + public int getHighlightSnippets() { + return this.getInt(HighlightParams.SNIPPETS, 1); + } + + public SolrQuery setHighlightFragsize(int num) { + this.set(HighlightParams.FRAGSIZE, num); + return this; + } + + public int getHighlightFragsize() { + return this.getInt(HighlightParams.FRAGSIZE, 100); + } + + public SolrQuery setHighlightRequireFieldMatch(boolean flag) { + this.set(HighlightParams.FIELD_MATCH, flag); + return this; + } + + public boolean getHighlightRequireFieldMatch() { + return this.getBool(HighlightParams.FIELD_MATCH, false); + } + + public SolrQuery setHighlightSimplePre(String f) { + this.set(HighlightParams.SIMPLE_PRE, f); + return this; + } + + public String getHighlightSimplePre() { + return this.get(HighlightParams.SIMPLE_PRE, ""); + } + + public SolrQuery setHighlightSimplePost(String f) { + this.set(HighlightParams.SIMPLE_POST, f); + return this; + } + + public String getHighlightSimplePost() { + return this.get(HighlightParams.SIMPLE_POST, ""); + } + + /** + * Gets the raw sort field, as it will be sent to Solr. + * <p> + * The returned sort field will always contain a serialized version + * of the sort string built using {@link #setSort(SortClause)}, + * {@link #addSort(SortClause)}, {@link #addOrUpdateSort(SortClause)}, + * {@link #removeSort(SortClause)}, {@link #clearSorts()} and + * {@link #setSorts(List)}. + */ + public String getSortField() { + return this.get(CommonParams.SORT); + } + + /** + * Clears current sort information. + * + * @return the modified SolrQuery object, for easy chaining + * @since 4.2 + */ + public SolrQuery clearSorts() { + sortClauses = null; + serializeSorts(); + return this; + } + + /** + * Replaces the current sort information. + * + * @return the modified SolrQuery object, for easy chaining + * @since 4.2 + */ + public SolrQuery setSorts(List<SortClause> value) { + sortClauses = new ArrayList<>(value); + serializeSorts(); + return this; + } + + /** + * Gets an a list of current sort clauses. + * + * @return an immutable list of current sort clauses + * @since 4.2 + */ + public List<SortClause> getSorts() { + if (sortClauses == null) return Collections.emptyList(); + else return Collections.unmodifiableList(sortClauses); + } + + /** + * Replaces the current sort information with a single sort clause + * + * @return the modified SolrQuery object, for easy chaining + * @since 4.2 + */ + public SolrQuery setSort(String field, ORDER order) { + return setSort(new SortClause(field, order)); + } + + /** + * Replaces the current sort information with a single sort clause + * + * @return the modified SolrQuery object, for easy chaining + * @since 4.2 + */ + public SolrQuery setSort(SortClause sortClause) { + clearSorts(); + return addSort(sortClause); + } + + /** + * Adds a single sort clause to the end of the current sort information. + * + * @return the modified SolrQuery object, for easy chaining + * @since 4.2 + */ + public SolrQuery addSort(String field, ORDER order) { + return addSort(new SortClause(field, order)); + } + + /** + * Adds a single sort clause to the end of the query. + * + * @return the modified SolrQuery object, for easy chaining + * @since 4.2 + */ + public SolrQuery addSort(SortClause sortClause) { + if (sortClauses == null) sortClauses = new ArrayList<>(); + sortClauses.add(sortClause); + serializeSorts(); + return this; + } + + /** + * Updates or adds a single sort clause to the query. + * If the field is already used for sorting, the order + * of the existing field is modified; otherwise, it is + * added to the end. + * <p> + * @return the modified SolrQuery object, for easy chaining + * @since 4.2 + */ + public SolrQuery addOrUpdateSort(String field, ORDER order) { + return addOrUpdateSort(new SortClause(field, order)); + } + + /** + * Updates or adds a single sort field specification to the current sort + * information. If the sort field already exist in the sort information map, + * its position is unchanged and the sort order is set; if it does not exist, + * it is appended at the end with the specified order.. + * + * @return the modified SolrQuery object, for easy chaining + * @since 4.2 + */ + public SolrQuery addOrUpdateSort(SortClause sortClause) { + if (sortClauses != null) { + for (int index=0 ; index<sortClauses.size() ; index++) { + SortClause existing = sortClauses.get(index); + if (existing.getItem().equals(sortClause.getItem())) { + sortClauses.set(index, sortClause); + serializeSorts(); + return this; + } + } + } + return addSort(sortClause); + } + + /** + * Removes a single sort field from the current sort information. + * + * @return the modified SolrQuery object, for easy chaining + * @since 4.2 + */ + public SolrQuery removeSort(SortClause sortClause) { + return removeSort(sortClause.getItem()); + } + + /** + * Removes a single sort field from the current sort information. + * + * @return the modified SolrQuery object, for easy chaining + * @since 4.2 + */ + public SolrQuery removeSort(String itemName) { + if (sortClauses != null) { + for (SortClause existing : sortClauses) { + if (existing.getItem().equals(itemName)) { + sortClauses.remove(existing); + if (sortClauses.isEmpty()) sortClauses = null; + serializeSorts(); + break; + } + } + } + return this; + } + + private void serializeSorts() { + if (sortClauses == null || sortClauses.isEmpty()) { + remove(CommonParams.SORT); + } else { + StringBuilder sb = new StringBuilder(); + for (SortClause sortClause : sortClauses) { + if (sb.length() > 0) sb.append(","); + sb.append(sortClause.getItem()); + sb.append(" "); + sb.append(sortClause.getOrder()); + } + set(CommonParams.SORT, sb.toString()); + } + } + + public void setGetFieldStatistics( boolean v ) + { + this.set( StatsParams.STATS, v ); + } + + public void setGetFieldStatistics( String field ) + { + this.set( StatsParams.STATS, true ); + this.add( StatsParams.STATS_FIELD, field ); + } + + + public void addGetFieldStatistics( String ... field ) + { + this.set( StatsParams.STATS, true ); + this.add( StatsParams.STATS_FIELD, field ); + } + + public void addStatsFieldFacets( String field, String ... facets ) + { + if( field == null ) { + this.add( StatsParams.STATS_FACET, facets ); + } + else { + for( String f : facets ) { + this.add( "f."+field+"."+StatsParams.STATS_FACET, f ); + } + } + } + + public void addStatsFieldCalcDistinct(String field, boolean calcDistinct) { + if (field == null) { + this.add(StatsParams.STATS_CALC_DISTINCT, Boolean.toString(calcDistinct)); + } else { + this.add("f." + field + "." + StatsParams.STATS_CALC_DISTINCT, Boolean.toString(calcDistinct)); + } + } + + public SolrQuery setFilterQueries(String ... fq) { + this.set(CommonParams.FQ, fq); + return this; + } + + public SolrQuery addFilterQuery(String ... fq) { + this.add(CommonParams.FQ, fq); + return this; + } + + public boolean removeFilterQuery(String fq) { + return this.remove(CommonParams.FQ, fq); + } + + public String[] getFilterQueries() { + return this.getParams(CommonParams.FQ); + } + + public boolean getHighlight() { + return this.getBool(HighlightParams.HIGHLIGHT, false); + } + + public SolrQuery setHighlight(boolean b) { + if (b) { + this.set(HighlightParams.HIGHLIGHT, true); + } else { + this.remove(HighlightParams.HIGHLIGHT); + this.remove(HighlightParams.FIELD_MATCH); + this.remove(HighlightParams.FIELDS); + this.remove(HighlightParams.FORMATTER); + this.remove(HighlightParams.FRAGSIZE); + this.remove(HighlightParams.SIMPLE_POST); + this.remove(HighlightParams.SIMPLE_PRE); + this.remove(HighlightParams.SNIPPETS); + } + return this; + } + + public SolrQuery setFields(String ... fields) { + if( fields == null || fields.length == 0 ) { + this.remove( CommonParams.FL ); + return this; + } + StringBuilder sb = new StringBuilder(); + sb.append( fields[0] ); + for( int i=1; i<fields.length; i++ ) { + sb.append( ',' ); + sb.append( fields[i] ); + } + this.set(CommonParams.FL, sb.toString() ); + return this; + } + + public SolrQuery addField(String field) { + return addValueToParam(CommonParams.FL, field); + } + + public String getFields() { + String fields = this.get(CommonParams.FL); + if (fields!=null && fields.equals("score")) { + fields = "*, score"; + } + return fields; + } + + private static Pattern scorePattern = Pattern.compile("(^|[, ])score"); + + public SolrQuery setIncludeScore(boolean includeScore) { + String fields = get(CommonParams.FL,"*"); + if (includeScore) { + if (!scorePattern.matcher(fields).find()) { + this.set(CommonParams.FL, fields+",score"); + } + } else { + this.set(CommonParams.FL, scorePattern.matcher(fields).replaceAll("")); + } + return this; + } + + public SolrQuery setQuery(String query) { + this.set(CommonParams.Q, query); + return this; + } + + public String getQuery() { + return this.get(CommonParams.Q); + } + + public SolrQuery setRows(Integer rows) { + if( rows == null ) { + this.remove( CommonParams.ROWS ); + } + else { + this.set(CommonParams.ROWS, rows); + } + return this; + } + + public Integer getRows() + { + return this.getInt(CommonParams.ROWS); + } + + public void setShowDebugInfo(boolean showDebugInfo) { + this.set(CommonParams.DEBUG_QUERY, String.valueOf(showDebugInfo)); + } + + public void setDistrib(boolean val) { + this.set(CommonParams.DISTRIB, String.valueOf(val)); + } + + + public SolrQuery setStart(Integer start) { + if( start == null ) { + this.remove( CommonParams.START ); + } + else { + this.set(CommonParams.START, start); + } + return this; + } + + public Integer getStart() + { + return this.getInt(CommonParams.START); + } + + /** + * The Request Handler to use (see the solrconfig.xml), which is stored in the "qt" parameter. + * Normally it starts with a '/' and if so it will be used by + * {@link org.apache.solr.client.solrj.request.QueryRequest#getPath()} in the URL instead of the "qt" parameter. + * If this is left blank, then the default of "/select" is assumed. + * + * @param qt The Request Handler name corresponding to one in solrconfig.xml on the server. + * @return this + */ + public SolrQuery setRequestHandler(String qt) { + this.set(CommonParams.QT, qt); + return this; + } + + public String getRequestHandler() { + return this.get(CommonParams.QT); + } + + /** + * @return this + * @see ModifiableSolrParams#set(String,String[]) + */ + public SolrQuery setParam(String name, String ... values) { + this.set(name, values); + return this; + } + + /** + * @return this + * @see org.apache.solr.common.params.ModifiableSolrParams#set(String, boolean) + */ + public SolrQuery setParam(String name, boolean value) { + this.set(name, value); + return this; + } + + /** get a deep copy of this object **/ + public SolrQuery getCopy() { + SolrQuery q = new SolrQuery(); + for (String name : this.getParameterNames()) { + q.setParam(name, this.getParams(name)); + } + return q; + } + + /** + * Set the maximum time allowed for this query. If the query takes more time + * than the specified milliseconds, a timeout occurs and partial (or no) + * results may be returned. + * + * If given Integer is null, then this parameter is removed from the request + * + *@param milliseconds the time in milliseconds allowed for this query + */ + public SolrQuery setTimeAllowed(Integer milliseconds) { + if (milliseconds == null) { + this.remove(CommonParams.TIME_ALLOWED); + } else { + this.set(CommonParams.TIME_ALLOWED, milliseconds); + } + return this; + } + + /** + * Get the maximum time allowed for this query. + */ + public Integer getTimeAllowed() { + return this.getInt(CommonParams.TIME_ALLOWED); + } + + /////////////////////// + // Utility functions + /////////////////////// + + private String toSortString(String field, ORDER order) { + return field.trim() + ' ' + String.valueOf(order).trim(); + } + + private String join(String a, String b, String sep) { + StringBuilder sb = new StringBuilder(); + if (a!=null && a.length()>0) { + sb.append(a); + sb.append(sep); + } + if (b!=null && b.length()>0) { + sb.append(b); + } + return sb.toString().trim(); + } + + private SolrQuery addValueToParam(String name, String value) { + String tmp = this.get(name); + tmp = join(tmp, value, ","); + this.set(name, tmp); + return this; + } + + private String join(String[] vals, String sep, String removeVal) { + StringBuilder sb = new StringBuilder(); + for (int i=0; i<vals.length; i++) { + if (!vals[i].equals(removeVal)) { + if (sb.length() > 0) { + sb.append(sep); + } + sb.append(vals[i]); + } + } + return sb.toString().trim(); + } + + /** + * A single sort clause, encapsulating what to sort and the sort order. + * <p> + * The item specified can be "anything sortable" by solr; some examples + * include a simple field name, the constant string {@code score}, and functions + * such as {@code sum(x_f, y_f)}. + * <p> + * A SortClause can be created through different mechanisms: + * <PRE><code> + * new SortClause("product", SolrQuery.ORDER.asc); + * new SortClause("product", "asc"); + * SortClause.asc("product"); + * SortClause.desc("product"); + * </code></PRE> + */ + public static class SortClause implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + private final String item; + private final ORDER order; + + /** + * Creates a SortClause based on item and order + * @param item item to sort on + * @param order direction to sort + */ + public SortClause(String item, ORDER order) { + this.item = item; + this.order = order; + } + + /** + * Creates a SortClause based on item and order + * @param item item to sort on + * @param order string value for direction to sort + */ + public SortClause(String item, String order) { + this(item, ORDER.valueOf(order)); + } + + /** + * Creates an ascending SortClause for an item + * @param item item to sort on + */ + public static SortClause create (String item, ORDER order) { + return new SortClause(item, order); + } + + /** + * Creates a SortClause based on item and order + * @param item item to sort on + * @param order string value for direction to sort + */ + public static SortClause create(String item, String order) { + return new SortClause(item, ORDER.valueOf(order)); + } + + /** + * Creates an ascending SortClause for an item + * @param item item to sort on + */ + public static SortClause asc (String item) { + return new SortClause(item, ORDER.asc); + } + + /** + * Creates a decending SortClause for an item + * @param item item to sort on + */ + public static SortClause desc (String item) { + return new SortClause(item, ORDER.desc); + } + + /** + * Gets the item to sort, typically a function or a fieldname + * @return item to sort + */ + public String getItem() { + return item; + } + + /** + * Gets the order to sort + * @return order to sort + */ + public ORDER getOrder() { + return order; + } + + public boolean equals(Object other){ + if (this == other) return true; + if (!(other instanceof SortClause)) return false; + final SortClause that = (SortClause) other; + return this.getItem().equals(that.getItem()) && this.getOrder().equals(that.getOrder()); + } + + public int hashCode(){ + return this.getItem().hashCode(); + } + + /** + * Gets a human readable description of the sort clause. + * <p> + * The returned string is not suitable for passing to Solr, + * but may be useful in debug output and the like. + * @return a description of the current sort clause + */ + public String toString() { + return "[" + getClass().getSimpleName() + ": item=" + getItem() + "; order=" + getOrder() + "]"; + } + } +}
http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrRequest.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrRequest.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrRequest.java new file mode 100644 index 0000000..6d4efb6 --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrRequest.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj; + +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.ContentStream; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * + * + * @since solr 1.3 + */ +public abstract class SolrRequest<T extends SolrResponse> implements Serializable { + + public enum METHOD { + GET, + POST, + PUT + }; + + private METHOD method = METHOD.GET; + private String path = null; + + private ResponseParser responseParser; + private StreamingResponseCallback callback; + private Set<String> queryParams; + + //--------------------------------------------------------- + //--------------------------------------------------------- + + public SolrRequest( METHOD m, String path ) + { + this.method = m; + this.path = path; + } + + //--------------------------------------------------------- + //--------------------------------------------------------- + + public METHOD getMethod() { + return method; + } + public void setMethod(METHOD method) { + this.method = method; + } + + public String getPath() { + return path; + } + public void setPath(String path) { + this.path = path; + } + + /** + * + * @return The {@link org.apache.solr.client.solrj.ResponseParser} + */ + public ResponseParser getResponseParser() { + return responseParser; + } + + /** + * Optionally specify how the Response should be parsed. Not all server implementations require a ResponseParser + * to be specified. + * @param responseParser The {@link org.apache.solr.client.solrj.ResponseParser} + */ + public void setResponseParser(ResponseParser responseParser) { + this.responseParser = responseParser; + } + + public StreamingResponseCallback getStreamingResponseCallback() { + return callback; + } + + public void setStreamingResponseCallback(StreamingResponseCallback callback) { + this.callback = callback; + } + + /** + * Parameter keys that are sent via the query string + */ + public Set<String> getQueryParams() { + return this.queryParams; + } + + public void setQueryParams(Set<String> queryParams) { + this.queryParams = queryParams; + } + + public abstract SolrParams getParams(); + + public abstract Collection<ContentStream> getContentStreams() throws IOException; + + /** + * Create a new SolrResponse to hold the response from the server + * @param client the {@link SolrClient} the request will be sent to + */ + protected abstract T createResponse(SolrClient client); + + /** + * Send this request to a {@link SolrClient} and return the response + * @param client the SolrClient to communicate with + * @return the response + * @throws SolrServerException if there is an error on the Solr server + * @throws IOException if there is a communication error + */ + public final T process(SolrClient client) throws SolrServerException, IOException { + long startTime = TimeUnit.MILLISECONDS.convert(System.nanoTime(), TimeUnit.NANOSECONDS); + T res = createResponse(client); + res.setResponse(client.request(this)); + long endTime = TimeUnit.MILLISECONDS.convert(System.nanoTime(), TimeUnit.NANOSECONDS); + res.setElapsedTime(endTime - startTime); + return res; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrResponse.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrResponse.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrResponse.java new file mode 100644 index 0000000..244a757 --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrResponse.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj; + +import org.apache.solr.common.SolrException; +import org.apache.solr.common.SolrException.ErrorCode; +import org.apache.solr.common.util.NamedList; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + + +/** + * + * + * @since solr 1.3 + */ +public abstract class SolrResponse implements Serializable { + public abstract long getElapsedTime(); + + public abstract void setResponse(NamedList<Object> rsp); + + public abstract void setElapsedTime(long elapsedTime); + + public abstract NamedList<Object> getResponse(); + + public static byte[] serializable(SolrResponse response) { + try { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + ObjectOutputStream outputStream = new ObjectOutputStream(byteStream); + outputStream.writeObject(response); + return byteStream.toByteArray(); + } catch (Exception e) { + throw new SolrException(ErrorCode.SERVER_ERROR, e); + } + } + + public static SolrResponse deserialize(byte[] bytes) { + try { + ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes); + ObjectInputStream inputStream = new ObjectInputStream(byteStream); + return (SolrResponse) inputStream.readObject(); + } catch (Exception e) { + throw new SolrException(ErrorCode.SERVER_ERROR, e); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrServer.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrServer.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrServer.java new file mode 100644 index 0000000..0238214 --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrServer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj; + +/** + * @deprecated Use {@link org.apache.solr.client.solrj.SolrClient} + */ +@Deprecated +public abstract class SolrServer extends SolrClient { +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrServerException.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrServerException.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrServerException.java new file mode 100644 index 0000000..ed16a43 --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/SolrServerException.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj; + +/** Exception to catch all types of communication / parsing issues associated with talking to SOLR + * + * + * @since solr 1.3 + */ +public class SolrServerException extends Exception { + + private static final long serialVersionUID = -3371703521752000294L; + + public SolrServerException(String message, Throwable cause) { + super(message, cause); + } + + public SolrServerException(String message) { + super(message); + } + + public SolrServerException(Throwable cause) { + super(cause); + } + + public Throwable getRootCause() { + Throwable t = this; + while (true) { + Throwable cause = t.getCause(); + if (cause!=null) { + t = cause; + } else { + break; + } + } + return t; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/StreamingResponseCallback.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/StreamingResponseCallback.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/StreamingResponseCallback.java new file mode 100644 index 0000000..2ba79c8 --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/StreamingResponseCallback.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj; + +import org.apache.solr.common.SolrDocument; + +/** + * A callback interface for streaming response + * + * @since solr 4.0 + */ +public abstract class StreamingResponseCallback { + /* + * Called for each SolrDocument in the response + */ + public abstract void streamSolrDocument( SolrDocument doc ); + + /* + * Called at the beginning of each DocList (and SolrDocumentList) + */ + public abstract void streamDocListInfo( long numFound, long start, Float maxScore ); +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/BindingException.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/BindingException.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/BindingException.java new file mode 100644 index 0000000..cd10138 --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/BindingException.java @@ -0,0 +1,29 @@ +package org.apache.solr.client.solrj.beans; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +public class BindingException extends RuntimeException { + + public BindingException(String message) { + super(message); + } + + public BindingException(String message, Throwable cause) { + super(message, cause); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java new file mode 100644 index 0000000..bff6c6a --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java @@ -0,0 +1,470 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj.beans; + +import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrInputDocument; + +import java.lang.reflect.*; +import java.util.*; +import java.util.regex.Pattern; +import java.util.concurrent.ConcurrentHashMap; +import java.nio.ByteBuffer; + +/** + * A class to map objects to and from solr documents. + * + * + * @since solr 1.3 + */ +public class DocumentObjectBinder { + + private final Map<Class, List<DocField>> infocache = new ConcurrentHashMap<>(); + + public DocumentObjectBinder() { + } + + public <T> List<T> getBeans(Class<T> clazz, SolrDocumentList solrDocList) { + List<DocField> fields = getDocFields(clazz); + List<T> result = new ArrayList<>(solrDocList.size()); + + for (SolrDocument sdoc : solrDocList) { + result.add(getBean(clazz, fields, sdoc)); + } + return result; + } + + public <T> T getBean(Class<T> clazz, SolrDocument solrDoc) { + return getBean(clazz, null, solrDoc); + } + + private <T> T getBean(Class<T> clazz, List<DocField> fields, SolrDocument solrDoc) { + if (fields == null) { + fields = getDocFields(clazz); + } + + try { + T obj = clazz.newInstance(); + for (DocField docField : fields) { + docField.inject(obj, solrDoc); + } + return obj; + } catch (Exception e) { + throw new BindingException("Could not instantiate object of " + clazz, e); + } + } + + public SolrInputDocument toSolrInputDocument(Object obj) { + List<DocField> fields = getDocFields(obj.getClass()); + if (fields.isEmpty()) { + throw new BindingException("class: " + obj.getClass() + " does not define any fields."); + } + + SolrInputDocument doc = new SolrInputDocument(); + for (DocField field : fields) { + if (field.dynamicFieldNamePatternMatcher != null && + field.get(obj) != null && + field.isContainedInMap) { + Map<String, Object> mapValue = (Map<String, Object>) field.get(obj); + + for (Map.Entry<String, Object> e : mapValue.entrySet()) { + doc.setField(e.getKey(), e.getValue(), 1.0f); + } + } else { + if (field.child != null) { + addChild(obj, field, doc); + } else { + doc.setField(field.name, field.get(obj), 1.0f); + } + } + } + return doc; + } + + private void addChild(Object obj, DocField field, SolrInputDocument doc) { + Object val = field.get(obj); + if (val == null) return; + if (val instanceof Collection) { + Collection collection = (Collection) val; + for (Object o : collection) { + SolrInputDocument child = toSolrInputDocument(o); + doc.addChildDocument(child); + } + } else if (val.getClass().isArray()) { + Object[] objs = (Object[]) val; + for (Object o : objs) doc.addChildDocument(toSolrInputDocument(o)); + } else { + doc.addChildDocument(toSolrInputDocument(val)); + } + } + + private List<DocField> getDocFields(Class clazz) { + List<DocField> fields = infocache.get(clazz); + if (fields == null) { + synchronized(infocache) { + infocache.put(clazz, fields = collectInfo(clazz)); + } + } + return fields; + } + + private List<DocField> collectInfo(Class clazz) { + List<DocField> fields = new ArrayList<>(); + Class superClazz = clazz; + List<AccessibleObject> members = new ArrayList<>(); + + while (superClazz != null && superClazz != Object.class) { + members.addAll(Arrays.asList(superClazz.getDeclaredFields())); + members.addAll(Arrays.asList(superClazz.getDeclaredMethods())); + superClazz = superClazz.getSuperclass(); + } + boolean childFieldFound = false; + for (AccessibleObject member : members) { + if (member.isAnnotationPresent(Field.class)) { + member.setAccessible(true); + DocField df = new DocField(member); + if (df.child != null) { + if (childFieldFound) + throw new BindingException(clazz.getName() + " cannot have more than one Field with child=true"); + childFieldFound = true; + } + fields.add(df); + } + } + return fields; + } + + private class DocField { + private Field annotation; + private String name; + private java.lang.reflect.Field field; + private Method setter; + private Method getter; + private Class type; + private boolean isArray; + private boolean isList; + private List<DocField> child; + + /* + * dynamic fields may use a Map based data structure to bind a given field. + * if a mapping is done using, "Map<String, List<String>> foo", <code>isContainedInMap</code> + * is set to <code>TRUE</code> as well as <code>isList</code> is set to <code>TRUE</code> + */ + private boolean isContainedInMap; + private Pattern dynamicFieldNamePatternMatcher; + + public DocField(AccessibleObject member) { + if (member instanceof java.lang.reflect.Field) { + field = (java.lang.reflect.Field) member; + } else { + setter = (Method) member; + } + annotation = member.getAnnotation(Field.class); + storeName(annotation); + storeType(); + + // Look for a matching getter + if (setter != null) { + String gname = setter.getName(); + if (gname.startsWith("set")) { + gname = "get" + gname.substring(3); + try { + getter = setter.getDeclaringClass().getMethod(gname, (Class[]) null); + } catch (Exception ex) { + // no getter -- don't worry about it... + if (type == Boolean.class) { + gname = "is" + setter.getName().substring(3); + try { + getter = setter.getDeclaringClass().getMethod(gname, (Class[]) null); + } catch(Exception ex2) { + // no getter -- don't worry about it... + } + } + } + } + } + } + + private void storeName(Field annotation) { + if (annotation.value().equals(DEFAULT)) { + if (field != null) { + name = field.getName(); + } else { + String setterName = setter.getName(); + if (setterName.startsWith("set") && setterName.length() > 3) { + name = setterName.substring(3, 4).toLowerCase(Locale.ROOT) + setterName.substring(4); + } else { + name = setter.getName(); + } + } + } else if (annotation.value().indexOf('*') >= 0) { //dynamic fields are annotated as @Field("categories_*") + //if the field was annotated as a dynamic field, convert the name into a pattern + //the wildcard (*) is supposed to be either a prefix or a suffix, hence the use of replaceFirst + name = annotation.value().replaceFirst("\\*", "\\.*"); + dynamicFieldNamePatternMatcher = Pattern.compile("^"+name+"$"); + } else { + name = annotation.value(); + } + } + + private void storeType() { + if (field != null) { + type = field.getType(); + } else { + Class[] params = setter.getParameterTypes(); + if (params.length != 1) { + throw new BindingException("Invalid setter method. Must have one and only one parameter"); + } + type = params[0]; + } + + if (type == Collection.class || type == List.class || type == ArrayList.class) { + isList = true; + if (annotation.child()) { + populateChild(field.getGenericType()); + } else { + type = Object.class; + } + } else if (type == byte[].class) { + //no op + } else if (type.isArray()) { + isArray = true; + if (annotation.child()) { + populateChild(type.getComponentType()); + } else { + type = type.getComponentType(); + } + } else if (type == Map.class || type == HashMap.class) { //corresponding to the support for dynamicFields + if (annotation.child()) throw new BindingException("Map should is not a valid type for a child document"); + isContainedInMap = true; + //assigned a default type + type = Object.class; + if (field != null) { + if (field.getGenericType() instanceof ParameterizedType) { + //check what are the generic values + ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType(); + Type[] types = parameterizedType.getActualTypeArguments(); + if (types != null && types.length == 2 && types[0] == String.class) { + //the key should always be String + //Raw and primitive types + if (types[1] instanceof Class) { + //the value could be multivalued then it is a List, Collection, ArrayList + if (types[1] == Collection.class || types[1] == List.class || types[1] == ArrayList.class) { + type = Object.class; + isList = true; + } else { + //else assume it is a primitive and put in the source type itself + type = (Class) types[1]; + } + } else if (types[1] instanceof ParameterizedType) { //Of all the Parameterized types, only List is supported + Type rawType = ((ParameterizedType) types[1]).getRawType(); + if (rawType == Collection.class || rawType == List.class || rawType == ArrayList.class) { + type = Object.class; + isList = true; + } + } else if (types[1] instanceof GenericArrayType) { //Array types + type = (Class) ((GenericArrayType) types[1]).getGenericComponentType(); + isArray = true; + } else { //Throw an Exception if types are not known + throw new BindingException("Allowed type for values of mapping a dynamicField are : " + + "Object, Object[] and List"); + } + } + } + } + } else { + if (annotation.child()) { + populateChild(type); + } + } + } + + private void populateChild(Type typ) { + if (typ == null) { + throw new RuntimeException("no type information available for" + (field == null ? setter : field)); + } + if (typ.getClass() == Class.class) {//of type class + type = (Class) typ; + } else if (typ instanceof ParameterizedType) { + type = (Class) ((ParameterizedType) typ).getActualTypeArguments()[0]; + } else { + throw new BindingException("Invalid type information available for" + (field == null ? setter : field)); + + } + child = getDocFields(type); + } + + /** + * Called by the {@link #inject} method to read the value(s) for a field + * This method supports reading of all "matching" fieldName's in the <code>SolrDocument</code> + * + * Returns <code>SolrDocument.getFieldValue</code> for regular fields, + * and <code>Map<String, List<Object>></code> for a dynamic field. The key is all matching fieldName's. + */ + @SuppressWarnings("unchecked") + private Object getFieldValue(SolrDocument solrDocument) { + if (child != null) { + List<SolrDocument> children = solrDocument.getChildDocuments(); + if (children == null || children.isEmpty()) return null; + if (isList) { + ArrayList list = new ArrayList(children.size()); + for (SolrDocument c : children) { + list.add(getBean(type, child, c)); + } + return list; + } else if (isArray) { + Object[] arr = (Object[]) Array.newInstance(type, children.size()); + for (int i = 0; i < children.size(); i++) { + arr[i] = getBean(type, child, children.get(i)); + } + return arr; + + } else { + return getBean(type, child, children.get(0)); + } + } + Object fieldValue = solrDocument.getFieldValue(name); + if (fieldValue != null) { + //this is not a dynamic field. so return the value + return fieldValue; + } + + if (dynamicFieldNamePatternMatcher == null) { + return null; + } + + //reading dynamic field values + Map<String, Object> allValuesMap = null; + List allValuesList = null; + if (isContainedInMap) { + allValuesMap = new HashMap<>(); + } else { + allValuesList = new ArrayList(); + } + + for (String field : solrDocument.getFieldNames()) { + if (dynamicFieldNamePatternMatcher.matcher(field).find()) { + Object val = solrDocument.getFieldValue(field); + if (val == null) { + continue; + } + + if (isContainedInMap) { + if (isList) { + if (!(val instanceof List)) { + List al = new ArrayList(); + al.add(val); + val = al; + } + } else if (isArray) { + if (!(val instanceof List)) { + Object[] arr = (Object[]) Array.newInstance(type, 1); + arr[0] = val; + val = arr; + } else { + val = Array.newInstance(type, ((List) val).size()); + } + } + allValuesMap.put(field, val); + } else { + if (val instanceof Collection) { + allValuesList.addAll((Collection) val); + } else { + allValuesList.add(val); + } + } + } + } + if (isContainedInMap) { + return allValuesMap.isEmpty() ? null : allValuesMap; + } else { + return allValuesList.isEmpty() ? null : allValuesList; + } + } + + <T> void inject(T obj, SolrDocument sdoc) { + Object val = getFieldValue(sdoc); + if(val == null) { + return; + } + + if (isArray && !isContainedInMap) { + List list; + if (val.getClass().isArray()) { + set(obj, val); + return; + } else if (val instanceof List) { + list = (List) val; + } else { + list = new ArrayList(); + list.add(val); + } + set(obj, list.toArray((Object[]) Array.newInstance(type, list.size()))); + } else if (isList && !isContainedInMap) { + if (!(val instanceof List)) { + List list = new ArrayList(); + list.add(val); + val = list; + } + set(obj, val); + } else if (isContainedInMap) { + if (val instanceof Map) { + set(obj, val); + } + } else { + set(obj, val); + } + + } + + private void set(Object obj, Object v) { + if (v != null && type == ByteBuffer.class && v.getClass() == byte[].class) { + v = ByteBuffer.wrap((byte[]) v); + } + try { + if (field != null) { + field.set(obj, v); + } else if (setter != null) { + setter.invoke(obj, v); + } + } + catch (Exception e) { + throw new BindingException("Exception while setting value : " + v + " on " + (field != null ? field : setter), e); + } + } + + public Object get(final Object obj) { + if (field != null) { + try { + return field.get(obj); + } catch (Exception e) { + throw new BindingException("Exception while getting value: " + field, e); + } + } else if (getter == null) { + throw new BindingException("Missing getter for field: " + name + " -- You can only call the 'get' for fields that have a field of 'get' method"); + } + + try { + return getter.invoke(obj, (Object[]) null); + } catch (Exception e) { + throw new BindingException("Exception while getting value: " + getter, e); + } + } + } + public static final String DEFAULT = "#default"; +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/Field.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/Field.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/Field.java new file mode 100644 index 0000000..39f6752 --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/Field.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj.beans; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.apache.solr.client.solrj.beans.DocumentObjectBinder.DEFAULT; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + + +/** + * This class can be used to annotate a field or a setter an any class + * and SlrJ would help you convert to SolrInputDocument and from SolrDocument + * + * @since solr 1.3 + */ +@Target({FIELD, METHOD}) +@Retention(RUNTIME) +public @interface Field { + boolean child() default false; + String value() default DEFAULT; +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/package-info.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/package-info.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/package-info.java new file mode 100644 index 0000000..890005f --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/beans/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/** + * Annotation based mapping of client objects to Solr documents. + */ +package org.apache.solr.client.solrj.beans; + + http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/impl/BinaryRequestWriter.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/impl/BinaryRequestWriter.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/impl/BinaryRequestWriter.java new file mode 100644 index 0000000..67274c2 --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/impl/BinaryRequestWriter.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj.impl; + +import org.apache.solr.client.solrj.SolrRequest; +import org.apache.solr.client.solrj.request.JavaBinUpdateRequestCodec; +import org.apache.solr.client.solrj.request.RequestWriter; +import org.apache.solr.client.solrj.request.UpdateRequest; +import org.apache.solr.common.util.ContentStream; + +import java.io.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * A RequestWriter which writes requests in the javabin format + * + * + * @see org.apache.solr.client.solrj.request.RequestWriter + * @since solr 1.4 + */ +public class BinaryRequestWriter extends RequestWriter { + + @Override + public Collection<ContentStream> getContentStreams(SolrRequest req) throws IOException { + if (req instanceof UpdateRequest) { + UpdateRequest updateRequest = (UpdateRequest) req; + if (isNull(updateRequest.getDocuments()) && + isNull(updateRequest.getDeleteByIdMap()) && + isNull(updateRequest.getDeleteQuery()) + && (updateRequest.getDocIterator() == null) ) { + return null; + } + List<ContentStream> l = new ArrayList<>(); + l.add(new LazyContentStream(updateRequest)); + return l; + } else { + return super.getContentStreams(req); + } + + } + + + @Override + public String getUpdateContentType() { + return "application/javabin"; + } + + @Override + public ContentStream getContentStream(final UpdateRequest request) throws IOException { + final BAOS baos = new BAOS(); + new JavaBinUpdateRequestCodec().marshal(request, baos); + + return new ContentStream() { + @Override + public String getName() { + return null; + } + + @Override + public String getSourceInfo() { + return "javabin"; + } + + @Override + public String getContentType() { + return "application/javabin"; + } + + @Override + public Long getSize() // size if we know it, otherwise null + { + return new Long(baos.size()); + } + + @Override + public InputStream getStream() { + return new ByteArrayInputStream(baos.getbuf(), 0, baos.size()); + } + + @Override + public Reader getReader() { + throw new RuntimeException("No reader available . this is a binarystream"); + } + }; + } + + + @Override + public void write(SolrRequest request, OutputStream os) throws IOException { + if (request instanceof UpdateRequest) { + UpdateRequest updateRequest = (UpdateRequest) request; + new JavaBinUpdateRequestCodec().marshal(updateRequest, os); + } + } + + /* + * A hack to get access to the protected internal buffer and avoid an additional copy + */ + class BAOS extends ByteArrayOutputStream { + byte[] getbuf() { + return super.buf; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/40aa090d/ranger_solrj/src/main/java/org/apache/solr/client/solrj/impl/BinaryResponseParser.java ---------------------------------------------------------------------- diff --git a/ranger_solrj/src/main/java/org/apache/solr/client/solrj/impl/BinaryResponseParser.java b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/impl/BinaryResponseParser.java new file mode 100644 index 0000000..4f3caf1 --- /dev/null +++ b/ranger_solrj/src/main/java/org/apache/solr/client/solrj/impl/BinaryResponseParser.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.solr.client.solrj.impl; + +import org.apache.solr.client.solrj.ResponseParser; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.JavaBinCodec; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +/** + * + * @since solr 1.3 + */ +public class BinaryResponseParser extends ResponseParser { + public static final String BINARY_CONTENT_TYPE = "application/octet-stream"; + + @Override + public String getWriterType() { + return "javabin"; + } + + @Override + public NamedList<Object> processResponse(InputStream body, String encoding) { + try { + return (NamedList<Object>) new JavaBinCodec().unmarshal(body); + } catch (IOException e) { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "parsing error", e); + + } + } + + @Override + public String getContentType() { + return BINARY_CONTENT_TYPE; + } + + @Override + public String getVersion() { + return "2"; + } + + @Override + public NamedList<Object> processResponse(Reader reader) { + throw new RuntimeException("Cannot handle character stream"); + } +}
