mirror of https://github.com/apache/lucene.git
SOLR-12965: Add facet support to JsonQueryRequest
This commit is contained in:
parent
4e2481b04b
commit
52998fa50e
|
@ -1,5 +1,7 @@
|
|||
= JSON Facet API
|
||||
:page-tocclass: right
|
||||
:solr-root-path: ../../
|
||||
:example-source-dir: {solr-root-path}solrj/src/test/org/apache/solr/client/ref_guide_examples/
|
||||
// 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
|
||||
|
@ -74,6 +76,11 @@ The response to the facet request above will start with documents matching the r
|
|||
|
||||
Here's an example of a bucketing facet, that partitions documents into bucket based on the `cat` field (short for category), and returns the top 3 buckets:
|
||||
|
||||
[.dynamic-tabs]
|
||||
--
|
||||
[example.tab-pane#curljsonsimpletermsfacet]
|
||||
====
|
||||
[.tab-label]*curl*
|
||||
[source,bash]
|
||||
----
|
||||
curl http://localhost:8983/solr/techproducts/query -d 'q=*:*&
|
||||
|
@ -85,6 +92,18 @@ json.facet={
|
|||
}
|
||||
}'
|
||||
----
|
||||
====
|
||||
|
||||
[example.tab-pane#solrjjsonsimpletermsfacet]
|
||||
====
|
||||
[.tab-label]*SolrJ*
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-json-simple-terms-facet]
|
||||
----
|
||||
====
|
||||
--
|
||||
|
||||
The response below shows us that 32 documents match the default root domain. and 12 documents have `cat:electronics`, 4 documents have `cat:currency`, etc.
|
||||
|
||||
|
@ -132,6 +151,11 @@ curl http://localhost:8983/solr/techproducts/query -d 'q=*:*&json.facet=
|
|||
|
||||
Another option is to use the JSON Request API to provide the entire request in JSON:
|
||||
|
||||
[.dynamic-tabs]
|
||||
--
|
||||
[example.tab-pane#curljsontermsfacet2]
|
||||
====
|
||||
[.tab-label]*curl*
|
||||
[source,bash]
|
||||
----
|
||||
curl http://localhost:8983/solr/techproducts/query -d '
|
||||
|
@ -144,6 +168,18 @@ curl http://localhost:8983/solr/techproducts/query -d '
|
|||
}
|
||||
'
|
||||
----
|
||||
====
|
||||
|
||||
[example.tab-pane#solrjjsontermsfacet2]
|
||||
====
|
||||
[.tab-label]*SolrJ*
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{example-source-dir}JsonRequestApiTest.java[tag=solrj-json-terms-facet2]
|
||||
----
|
||||
====
|
||||
--
|
||||
|
||||
=== JSON Extensions
|
||||
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DomainMap extends HashMap<String, Object> {
|
||||
|
||||
/**
|
||||
* Indicates that the domain should be narrowed by the specified filter
|
||||
*
|
||||
* May be called multiple times. Each added filter is retained and used to narrow the domain.
|
||||
*/
|
||||
public DomainMap withFilter(String filter) {
|
||||
if (filter == null) {
|
||||
throw new IllegalArgumentException("Parameter 'filter' must be non-null");
|
||||
}
|
||||
|
||||
if (! containsKey("filter")) {
|
||||
put("filter", new ArrayList<String>());
|
||||
}
|
||||
|
||||
final List<String> filterList = (List<String>) get("filter");
|
||||
filterList.add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the domain should be the following query
|
||||
*
|
||||
* May be called multiple times. Each specified query is retained and included in the domain.
|
||||
*/
|
||||
public DomainMap withQuery(String query) {
|
||||
if (query == null) {
|
||||
throw new IllegalArgumentException("Parameter 'query' must be non-null");
|
||||
}
|
||||
|
||||
if (! containsKey("query")) {
|
||||
put("query", new ArrayList<String>());
|
||||
}
|
||||
|
||||
final List<String> queryList = (List<String>) get("query");
|
||||
queryList.add(query);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a tag or tags that correspond to filters or queries to exclude from the domain
|
||||
*
|
||||
* May be called multiple times. Each exclude-string is retained and used for removing queries/filters from the
|
||||
* domain specification.
|
||||
*
|
||||
* @param excludeTagsValue a comma-delimited String containing filter/query tags to exclude
|
||||
*/
|
||||
public DomainMap withTagsToExclude(String excludeTagsValue) {
|
||||
if (excludeTagsValue == null) {
|
||||
throw new IllegalArgumentException("Parameter 'excludeTagValue' must be non-null");
|
||||
}
|
||||
|
||||
if (! containsKey("excludeTags")) {
|
||||
put("excludeTags", new ArrayList<String>());
|
||||
}
|
||||
|
||||
final List<String> excludeTagsList = (List<String>) get("excludeTags");
|
||||
excludeTagsList.add(excludeTagsValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the resulting domain will contain all parent documents of the children in the existing domain
|
||||
*
|
||||
* @param allParentsQuery a query used to identify all parent documents in the collection
|
||||
*/
|
||||
public DomainMap setBlockParentQuery(String allParentsQuery) {
|
||||
if (allParentsQuery == null) {
|
||||
throw new IllegalArgumentException("Parameter 'allParentsQuery' must be non-null");
|
||||
}
|
||||
|
||||
put("blockParent", allParentsQuery);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the resulting domain will contain all child documents of the parents in the current domain
|
||||
*
|
||||
* @param allChildrenQuery a query used to identify all child documents in the collection
|
||||
*/
|
||||
public DomainMap setBlockChildQuery(String allChildrenQuery) {
|
||||
if (allChildrenQuery == null) {
|
||||
throw new IllegalArgumentException("Parameter 'allChildrenQuery' must be non-null");
|
||||
}
|
||||
|
||||
put("blockChildren", allChildrenQuery);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the domain by running a join query with the provided {@code from} and {@code to} parameters
|
||||
*
|
||||
* Join modifies the current domain by selecting the documents whose values in field {@code to} match values for the
|
||||
* field {@code from} in the current domain.
|
||||
*
|
||||
* @param from a field-name whose values are matched against {@code to} by the join
|
||||
* @param to a field name whose values should match values specified by the {@code from} field
|
||||
*/
|
||||
public DomainMap setJoinTransformation(String from, String to) {
|
||||
if (from == null) {
|
||||
throw new IllegalArgumentException("Parameter 'from' must be non-null");
|
||||
}
|
||||
if (to == null) {
|
||||
throw new IllegalArgumentException("Parameter 'to' must be non-null");
|
||||
}
|
||||
|
||||
final Map<String, Object> joinParameters = new HashMap<>();
|
||||
joinParameters.put("from", from);
|
||||
joinParameters.put("to", to);
|
||||
put("join", joinParameters);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -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.request.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a "heatmap" facet in a JSON request query.
|
||||
*
|
||||
* Ready for use with {@link JsonQueryRequest#withFacet(String, Map)}
|
||||
*/
|
||||
public class HeatmapFacetMap extends JsonFacetMap<HeatmapFacetMap> {
|
||||
public HeatmapFacetMap(String fieldName) {
|
||||
super("heatmap");
|
||||
|
||||
if (fieldName == null) {
|
||||
throw new IllegalArgumentException("Parameter 'fieldName' must be non-null");
|
||||
}
|
||||
|
||||
put("field", fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeatmapFacetMap getThis() { return this; }
|
||||
|
||||
@Override
|
||||
public HeatmapFacetMap withSubFacet(String facetName, JsonFacetMap map) {
|
||||
throw new UnsupportedOperationException(getClass().getName() + " doesn't currently support subfacets");
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the region to compute the heatmap facet on.
|
||||
*
|
||||
* Defaults to the "world" ("[-180,-90 TO 180,90]")
|
||||
*/
|
||||
public HeatmapFacetMap setRegionQuery(String queryString) {
|
||||
if (queryString == null) {
|
||||
throw new IllegalArgumentException("Parameter 'queryString' must be non-null");
|
||||
}
|
||||
|
||||
put("geom", queryString);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the size of each cell in the computed heatmap grid
|
||||
*
|
||||
* If not set, defaults to being computed by {@code distErrPct} or {@code distErr}
|
||||
*
|
||||
* @param individualCellSize the forced size of each cell in the heatmap grid
|
||||
*
|
||||
* @see #setDistErr(double)
|
||||
* @see #setDistErrPct(double)
|
||||
*/
|
||||
public HeatmapFacetMap setGridLevel(int individualCellSize) {
|
||||
if (individualCellSize <= 0) {
|
||||
throw new IllegalArgumentException("Parameter 'individualCellSize' must be a positive integer");
|
||||
}
|
||||
put("gridLevel", individualCellSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A fraction of the heatmap region that is used to compute the cell size.
|
||||
*
|
||||
* Defaults to 0.15 if not specified.
|
||||
*
|
||||
* @see #setGridLevel(int)
|
||||
* @see #setDistErr(double)
|
||||
*/
|
||||
public HeatmapFacetMap setDistErrPct(double distErrPct) {
|
||||
if (distErrPct < 0 || distErrPct > 1) {
|
||||
throw new IllegalArgumentException("Parameter 'distErrPct' must be between 0.0 and 1.0");
|
||||
}
|
||||
put("distErrPct", distErrPct);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the maximum acceptable cell error distance.
|
||||
*
|
||||
* Used to compute the size of each cell in the heatmap grid rather than specifying {@link #setGridLevel(int)}
|
||||
*
|
||||
* @param distErr a positive value representing the maximum acceptable cell error.
|
||||
*
|
||||
* @see #setGridLevel(int)
|
||||
* @see #setDistErrPct(double)
|
||||
*/
|
||||
public HeatmapFacetMap setDistErr(double distErr) {
|
||||
if (distErr < 0) {
|
||||
throw new IllegalArgumentException("Parameter 'distErr' must be non-negative");
|
||||
}
|
||||
put("distErr", distErr);
|
||||
return this;
|
||||
}
|
||||
|
||||
public enum HeatmapFormat {
|
||||
INTS2D("ints2D"), PNG("png");
|
||||
|
||||
private final String value;
|
||||
|
||||
HeatmapFormat(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return value; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the format that the computed heatmap should be returned in.
|
||||
*
|
||||
* Defaults to 'ints2D' if not specified.
|
||||
*/
|
||||
public HeatmapFacetMap setHeatmapFormat(HeatmapFormat format) {
|
||||
if (format == null) {
|
||||
throw new IllegalArgumentException("Parameter 'format' must be non-null");
|
||||
}
|
||||
put("format", format.toString());
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A common parent for a small set of classes that allow easier composition of JSON facet objects.
|
||||
*
|
||||
* Designed for use with {@link JsonQueryRequest#withFacet(String, Map)}
|
||||
*/
|
||||
public abstract class JsonFacetMap<B extends JsonFacetMap<B>> extends HashMap<String, Object> {
|
||||
|
||||
public abstract B getThis(); // Allows methods shared here to return subclass type
|
||||
|
||||
public JsonFacetMap(String facetType) {
|
||||
super();
|
||||
|
||||
put("type", facetType);
|
||||
}
|
||||
|
||||
public B withDomain(DomainMap domain) {
|
||||
put("domain", domain);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public B withSubFacet(String facetName, JsonFacetMap map) {
|
||||
if (! containsKey("facet")) {
|
||||
put("facet", new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
final Map<String, Object> subFacetMap = (Map<String, Object>) get("facet");
|
||||
subFacetMap.put(facetName, map);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public B withStatSubFacet(String facetName, String statFacet) {
|
||||
if (! containsKey("facet")) {
|
||||
put("facet", new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
final Map<String, Object> subFacetMap = (Map<String, Object>) get("facet");
|
||||
subFacetMap.put(facetName, statFacet);
|
||||
return getThis();
|
||||
}
|
||||
}
|
|
@ -132,6 +132,115 @@ public class JsonQueryRequest extends QueryRequest {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a facet sent as a part of this JSON request.
|
||||
*
|
||||
* This method may be called multiple times. Each call made with a different {@code facetName} value will add a new
|
||||
* top-level facet. Repeating {@code facetName} values will cause previous facets with that {@code facetName} to be
|
||||
* overwritten.
|
||||
* <p>
|
||||
* <b>Example:</b> You wish to send the JSON request: {"query": "*:*", "facet": { "top_cats":{"type": "terms", "field":"cat"}}}. You
|
||||
* would represent (and attach) the facet in this request as follows:
|
||||
* <pre>{@code
|
||||
* final Map<String, Object> catFacetMap = new HashMap<>();
|
||||
* catFacetMap.put("type", "terms");
|
||||
* catFacetMap.put("field", "cat");
|
||||
*
|
||||
* jsonQueryRequest.withStatFacet("top_cats", catFacetMap);
|
||||
* }</pre>
|
||||
*
|
||||
* @param facetName the name of the top-level facet you'd like to add.
|
||||
* @param facetJson a Map of values representing the facet you wish to add to the request
|
||||
*/
|
||||
public JsonQueryRequest withFacet(String facetName, Map<String, Object> facetJson) {
|
||||
if (facetName == null) {
|
||||
throw new IllegalArgumentException("'facetName' parameter must be non-null");
|
||||
}
|
||||
if (facetJson == null) {
|
||||
throw new IllegalArgumentException("'facetMap' parameter must be non-null");
|
||||
}
|
||||
|
||||
if (! jsonRequestMap.containsKey("facet")) {
|
||||
jsonRequestMap.put("facet", new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
final Map<String, Object> facetMap = (Map<String, Object>) jsonRequestMap.get("facet");
|
||||
facetMap.put(facetName, facetJson);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a facet sent as a part of this JSON request.
|
||||
*
|
||||
* This method may be called multiple times. Each call made with a different {@code facetName} value will add a new
|
||||
* top-level facet. Repeating {@code facetName} values will cause previous facets with that {@code facetName} to be
|
||||
* overwritten.
|
||||
* <p>
|
||||
* <b>Example:</b> You wish to send the JSON request: {"query": "*:*", "facet": { "top_cats":{"type": "terms", "field":"cat"}}}. You
|
||||
* would represent the facet in this request as follows:
|
||||
* <pre>
|
||||
* final MapWriter facetWriter = new MapWriter() {
|
||||
* @Override
|
||||
* public void writeMap(EntryWriter ew) throws IOException {
|
||||
* ew.put("type", "terms");
|
||||
* ew.put("field", "cat");
|
||||
* }
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* @param facetName the name of the top-level facet you'd like to add.
|
||||
* @param facetWriter a MapWriter representing the facet you wish to add to the request
|
||||
*/
|
||||
public JsonQueryRequest withFacet(String facetName, MapWriter facetWriter) {
|
||||
if (facetName == null) {
|
||||
throw new IllegalArgumentException("'facetName' parameter must be non-null");
|
||||
}
|
||||
if (facetWriter == null) {
|
||||
throw new IllegalArgumentException("'facetWriter' parameter must be non-null");
|
||||
}
|
||||
|
||||
if (! jsonRequestMap.containsKey("facet")) {
|
||||
jsonRequestMap.put("facet", new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
final Map<String, Object> facetMap = (Map<String, Object>) jsonRequestMap.get("facet");
|
||||
facetMap.put(facetName, facetWriter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a simple stat or aggregation facet to be sent as a part of this JSON request.
|
||||
*
|
||||
* This method may be called multiple times. Each call made with a different {@code facetName} value will add a new
|
||||
* top-level facet. Repeating {@code facetName} values will cause previous facets with that {@code facetName} to be
|
||||
* overwritten.
|
||||
* <p>
|
||||
* <b>Example:</b> You wish to send the JSON request: {"query": "*:*", "facet": {"avg_price": "avg(price)"}}. You
|
||||
* would represent the facet in this request as follows:
|
||||
* <pre>{@code
|
||||
* jsonQueryRequest.withStatFacet("avg_price", "avg(price)");
|
||||
* }</pre>
|
||||
*
|
||||
* @param facetName the name of the top-level stat/agg facet you'd like to add.
|
||||
* @param facetValue a String representing the stat/agg facet computation to perform.
|
||||
*/
|
||||
public JsonQueryRequest withStatFacet(String facetName, String facetValue) {
|
||||
if (facetName == null) {
|
||||
throw new IllegalArgumentException("'facetName' parameter must be non-null");
|
||||
}
|
||||
if (facetValue == null) {
|
||||
throw new IllegalArgumentException("'facetValue' parameter must be non-null");
|
||||
}
|
||||
|
||||
if (! jsonRequestMap.containsKey("facet")) {
|
||||
jsonRequestMap.put("facet", new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
final Map<String, Object> facetMap = (Map<String, Object>) jsonRequestMap.get("facet");
|
||||
facetMap.put(facetName, facetValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether results should be fetched starting from a particular offset (or 'start').
|
||||
*
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a "query" facet in a JSON query request.
|
||||
*
|
||||
* Ready for use in {@link JsonQueryRequest#withFacet(String, Map)}
|
||||
*/
|
||||
public class QueryFacetMap extends JsonFacetMap<QueryFacetMap> {
|
||||
public QueryFacetMap(String queryString) {
|
||||
super("query");
|
||||
|
||||
if (queryString == null) {
|
||||
throw new IllegalArgumentException("Parameter 'queryString' must be non-null");
|
||||
}
|
||||
put("q", queryString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryFacetMap getThis() { return this; }
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a "range" facet in a JSON request query.
|
||||
*
|
||||
* Ready for use with {@link JsonQueryRequest#withFacet(String, Map)}
|
||||
*/
|
||||
public class RangeFacetMap extends JsonFacetMap<RangeFacetMap> {
|
||||
public RangeFacetMap(String field, long start, long end, long gap) {
|
||||
super("range");
|
||||
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Parameter 'field' must be non-null");
|
||||
}
|
||||
if (end < start) {
|
||||
throw new IllegalArgumentException("Parameter 'end' must be greater than parameter 'start'");
|
||||
}
|
||||
if (gap <= 0) {
|
||||
throw new IllegalArgumentException("Parameter 'gap' must be a positive integer");
|
||||
}
|
||||
|
||||
put("field", field);
|
||||
put("start", start);
|
||||
put("end", end);
|
||||
put("gap", gap);
|
||||
}
|
||||
|
||||
public RangeFacetMap(String field, double start, double end, double gap) {
|
||||
super("range");
|
||||
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Parameter 'field' must be non-null");
|
||||
}
|
||||
if (end < start) {
|
||||
throw new IllegalArgumentException("Parameter 'end' must be greater than parameter 'start'");
|
||||
}
|
||||
if (gap <= 0) {
|
||||
throw new IllegalArgumentException("Parameter 'gap' must be a positive value");
|
||||
}
|
||||
|
||||
put("field", field);
|
||||
put("start", start);
|
||||
put("end", end);
|
||||
put("gap", gap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RangeFacetMap getThis() { return this; }
|
||||
|
||||
/**
|
||||
* Indicates whether the facet's last bucket should stop exactly at {@code end}, or be extended to be {@code gap} wide
|
||||
*
|
||||
* Defaults to false if not specified.
|
||||
*
|
||||
* @param hardEnd true if the final bucket should be truncated at {@code end}; false otherwise
|
||||
*/
|
||||
public RangeFacetMap setHardEnd(boolean hardEnd) {
|
||||
put("hardend", hardEnd);
|
||||
return this;
|
||||
}
|
||||
|
||||
public enum OtherBuckets {
|
||||
BEFORE("before"), AFTER("after"), BETWEEN("between"), NONE("none"), ALL("all");
|
||||
|
||||
private final String value;
|
||||
|
||||
OtherBuckets(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() { return value; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that an additional range bucket(s) should be computed and added to those computed for {@code start} and {@code end}
|
||||
*
|
||||
* See {@link OtherBuckets} for possible options.
|
||||
*/
|
||||
public RangeFacetMap setOtherBuckets(OtherBuckets bucketSpecifier) {
|
||||
if (bucketSpecifier == null) {
|
||||
throw new IllegalArgumentException("Parameter 'bucketSpecifier' must be non-null");
|
||||
}
|
||||
put("other", bucketSpecifier.toString());
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a "terms" facet in a JSON query request.
|
||||
*
|
||||
* Ready for use in {@link JsonQueryRequest#withFacet(String, Map)}
|
||||
*/
|
||||
public class TermsFacetMap extends JsonFacetMap<TermsFacetMap> {
|
||||
public TermsFacetMap(String fieldName) {
|
||||
super("terms");
|
||||
|
||||
put("field", fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TermsFacetMap getThis() { return this; }
|
||||
|
||||
/**
|
||||
* Indicates that Solr should skip over the N buckets for this facet.
|
||||
*
|
||||
* Used for "paging" in facet results. Defaults to 0 if not provided.
|
||||
*
|
||||
* @param numToSkip the number of buckets to skip over before selecting the buckets to return
|
||||
*/
|
||||
public TermsFacetMap setBucketOffset(int numToSkip) {
|
||||
if (numToSkip < 0) {
|
||||
throw new IllegalArgumentException("Parameter 'numToSkip' must be non-negative");
|
||||
}
|
||||
put("offset", numToSkip);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the maximum number of buckets to be returned by this facet.
|
||||
*
|
||||
* Defaults to 10 if not specified.
|
||||
*/
|
||||
public TermsFacetMap setLimit(int maximumBuckets) {
|
||||
if (maximumBuckets < 0) {
|
||||
throw new IllegalArgumentException("Parameter 'maximumBuckets' must be non-negative");
|
||||
}
|
||||
put("limit", maximumBuckets);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the desired ordering for the returned buckets.
|
||||
*
|
||||
* Values can be based on 'count' (the number of results in each bucket), 'index' (the natural order of bucket values),
|
||||
* or on any stat facet that occurs in the bucket. Defaults to "count desc" if not specified.
|
||||
*/
|
||||
public TermsFacetMap setSort(String sortString) {
|
||||
if (sortString == null) {
|
||||
throw new IllegalArgumentException("Parameter 'sortString' must be non-null");
|
||||
}
|
||||
put("sort", sortString);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the number of additional buckets to request internally beyond those required by {@link #setLimit(int)}.
|
||||
*
|
||||
* Defaults to -1 if not specified, which triggers some heuristic guessing based on other settings.
|
||||
*/
|
||||
public TermsFacetMap setOverRequest(int numExtraBuckets) {
|
||||
if (numExtraBuckets < -1) {
|
||||
throw new IllegalArgumentException("Parameter 'numExtraBuckets' must be >= -1");
|
||||
}
|
||||
put("overrequest", numExtraBuckets);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this facet should use distributed facet refining.
|
||||
*
|
||||
* "Distributed facet refining" is a second, optional stage in the facet process that ensures that counts for the
|
||||
* returned buckets are exact. Enabling it is a tradeoff between precision and speed/performance. Defaults to false
|
||||
* if not specified.
|
||||
* @param useRefining true if distributed facet refining should be used; false otherwise
|
||||
*/
|
||||
public TermsFacetMap useDistributedFacetRefining(boolean useRefining) {
|
||||
put("refine", useRefining);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates how many extra buckets to request during distributed-facet-refining beyond those required by {@link #setLimit(int)}
|
||||
*
|
||||
* Defaults to -1 if not specified, which triggers some heuristic guessing based on other settings.
|
||||
*/
|
||||
public TermsFacetMap setOverRefine(int numExtraBuckets) {
|
||||
if (numExtraBuckets < -1) {
|
||||
throw new IllegalArgumentException("Parameter 'numExtraBuckets' must be >= -1");
|
||||
}
|
||||
put("overrefine", numExtraBuckets);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the facet results should not include any buckets with a count less than {@code minCount}.
|
||||
*
|
||||
* Defaults to 1 if not specified.
|
||||
*/
|
||||
public TermsFacetMap setMinCount(int minCount) {
|
||||
if (minCount < 1) {
|
||||
throw new IllegalArgumentException("Parameter 'minCount' must be a positive integer");
|
||||
}
|
||||
put("mincount", minCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that Solr should create a bucket corresponding to documents missing the field used by this facet.
|
||||
*
|
||||
* Defaults to false if not specified.
|
||||
*
|
||||
* @param missingBucket true if the special "missing" bucket should be created; false otherwise
|
||||
*/
|
||||
public TermsFacetMap includeMissingBucket(boolean missingBucket) {
|
||||
put("missing", missingBucket);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that Solr should include the total number of buckets for this facet.
|
||||
*
|
||||
* Note that this is different than the number of buckets returned. Defaults to false if not specified
|
||||
*
|
||||
* @param numBuckets true if the "numBuckets" field should be computed; false otherwise
|
||||
*/
|
||||
public TermsFacetMap includeTotalNumBuckets(boolean numBuckets) {
|
||||
put("numBuckets", numBuckets);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bucket representing the union of all other buckets.
|
||||
*
|
||||
* For multi-valued fields this is different than a bucket for the entire domain, since documents can belong to
|
||||
* multiple buckets. Defaults to false if not specified.
|
||||
*
|
||||
* @param shouldInclude true if the union bucket "allBuckets" should be computed; false otherwise
|
||||
*/
|
||||
public TermsFacetMap includeAllBucketsUnionBucket(boolean shouldInclude) {
|
||||
put("allBuckets", shouldInclude);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the facet should only produce buckets for terms that start with the specified prefix.
|
||||
*/
|
||||
public TermsFacetMap setTermPrefix(String termPrefix) {
|
||||
if (termPrefix == null) {
|
||||
throw new IllegalArgumentException("Parameter 'termPrefix' must be non-null");
|
||||
}
|
||||
put("prefix", termPrefix);
|
||||
return this;
|
||||
}
|
||||
|
||||
public enum FacetMethod {
|
||||
DV("dv"), UIF("uif"), DVHASH("dvhash"), ENUM("enum"), STREAM("stream"), SMART("smart");
|
||||
|
||||
private final String value;
|
||||
FacetMethod(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate which method should be used to compute the facet.
|
||||
*
|
||||
* Defaults to "smart" if not specified, which has Solr guess which computation method will be most efficient.
|
||||
*/
|
||||
public TermsFacetMap setFacetMethod(FacetMethod method) {
|
||||
if (method == null) {
|
||||
throw new IllegalArgumentException("Parameter 'method' must be non-null");
|
||||
}
|
||||
put("method", method.toString());
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,421 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<add>
|
||||
<doc>
|
||||
<field name="id">TWINX2048-3200PRO</field>
|
||||
<field name="name">CORSAIR XMS 2GB (2 x 1GB) 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) Dual Channel Kit System Memory - Retail</field>
|
||||
<field name="manu">Corsair Microsystems Inc.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">corsair</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">memory</field>
|
||||
<field name="features">CAS latency 2, 2-3-3-6 timing, 2.75v, unbuffered, heat-spreader</field>
|
||||
<field name="price">185.00</field>
|
||||
<field name="popularity">5</field>
|
||||
<field name="inStock">true</field>
|
||||
<!-- San Francisco store -->
|
||||
<field name="store">37.7752,-122.4232</field>
|
||||
<field name="manufacturedate_dt">2006-02-13T15:26:37Z</field>
|
||||
<!-- a field for testing payload tagged text via DelimitedPayloadTokenFilter -->
|
||||
<field name="payloads">electronics|6.0 memory|3.0</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">VS1GB400C3</field>
|
||||
<field name="name">CORSAIR ValueSelect 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - Retail</field>
|
||||
<field name="manu">Corsair Microsystems Inc.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">corsair</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">memory</field>
|
||||
<field name="price">74.99</field>
|
||||
<field name="popularity">7</field>
|
||||
<field name="inStock">true</field>
|
||||
<!-- Dodge City store -->
|
||||
<field name="store">37.7752,-100.0232</field>
|
||||
<field name="manufacturedate_dt">2006-02-13T15:26:37Z</field>
|
||||
<field name="payloads">electronics|4.0 memory|2.0</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">VDBDB1A16</field>
|
||||
<field name="name">A-DATA V-Series 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - OEM</field>
|
||||
<field name="manu">A-DATA Technology Inc.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">corsair</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">memory</field>
|
||||
<field name="features">CAS latency 3, 2.7v</field>
|
||||
<!-- note: price & popularity is missing on this one -->
|
||||
<field name="popularity">0</field>
|
||||
<field name="inStock">true</field>
|
||||
<!-- Buffalo store -->
|
||||
<field name="store">45.18414,-93.88141</field>
|
||||
<field name="manufacturedate_dt">2006-02-13T15:26:37Z</field>
|
||||
<field name="payloads">electronics|0.9 memory|0.1</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">MA147LL/A</field>
|
||||
<field name="name">Apple 60 GB iPod with Video Playback Black</field>
|
||||
<field name="manu">Apple Computer Inc.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">apple</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">music</field>
|
||||
<field name="features">iTunes, Podcasts, Audiobooks</field>
|
||||
<field name="features">Stores up to 15,000 songs, 25,000 photos, or 150 hours of video</field>
|
||||
<field name="features">2.5-inch, 320x240 color TFT LCD display with LED backlight</field>
|
||||
<field name="features">Up to 20 hours of battery life</field>
|
||||
<field name="features">Plays AAC, MP3, WAV, AIFF, Audible, Apple Lossless, H.264 video</field>
|
||||
<field name="features">Notes, Calendar, Phone book, Hold button, Date display, Photo wallet, Built-in games, JPEG photo playback, Upgradeable firmware, USB 2.0 compatibility, Playback speed control, Rechargeable capability, Battery level indication</field>
|
||||
<field name="includes">earbud headphones, USB cable</field>
|
||||
<field name="weight">5.5</field>
|
||||
<field name="price">399.00</field>
|
||||
<field name="popularity">10</field>
|
||||
<field name="inStock">true</field>
|
||||
<!-- Dodge City store -->
|
||||
<field name="store">37.7752,-100.0232</field>
|
||||
<field name="manufacturedate_dt">2005-10-12T08:00:00Z</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">F8V7067-APL-KIT</field>
|
||||
<field name="name">Belkin Mobile Power Cord for iPod w/ Dock</field>
|
||||
<field name="manu">Belkin</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">belkin</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">connector</field>
|
||||
<field name="features">car power adapter, white</field>
|
||||
<field name="weight">4.0</field>
|
||||
<field name="price">19.95</field>
|
||||
<field name="popularity">1</field>
|
||||
<field name="inStock">false</field>
|
||||
<!-- Buffalo store -->
|
||||
<field name="store">45.18014,-93.87741</field>
|
||||
<field name="manufacturedate_dt">2005-08-01T16:30:25Z</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">IW-02</field>
|
||||
<field name="name">iPod & iPod Mini USB 2.0 Cable</field>
|
||||
<field name="manu">Belkin</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">belkin</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">connector</field>
|
||||
<field name="features">car power adapter for iPod, white</field>
|
||||
<field name="weight">2.0</field>
|
||||
<field name="price">11.50</field>
|
||||
<field name="popularity">1</field>
|
||||
<field name="inStock">false</field>
|
||||
<!-- San Francisco store -->
|
||||
<field name="store">37.7752,-122.4232</field>
|
||||
<field name="manufacturedate_dt">2006-02-14T23:55:59Z</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">9885A004</field>
|
||||
<field name="name">Canon PowerShot SD500</field>
|
||||
<field name="manu">Canon Inc.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">canon</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">camera</field>
|
||||
<field name="features">3x zoop, 7.1 megapixel Digital ELPH</field>
|
||||
<field name="features">movie clips up to 640x480 @30 fps</field>
|
||||
<field name="features">2.0" TFT LCD, 118,000 pixels</field>
|
||||
<field name="features">built in flash, red-eye reduction</field>
|
||||
<field name="includes">32MB SD card, USB cable, AV cable, battery</field>
|
||||
<field name="weight">6.4</field>
|
||||
<field name="price">329.95</field>
|
||||
<field name="popularity">7</field>
|
||||
<field name="inStock">true</field>
|
||||
<field name="manufacturedate_dt">2006-02-13T15:26:37Z</field>
|
||||
<!-- Buffalo store -->
|
||||
<field name="store">45.19614,-93.90341</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">VA902B</field>
|
||||
<field name="name">ViewSonic VA902B - flat panel display - TFT - 19"</field>
|
||||
<field name="manu">ViewSonic Corp.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">viewsonic</field>
|
||||
<field name="cat">electronics and stuff2</field>
|
||||
<field name="features">19" TFT active matrix LCD, 8ms response time, 1280 x 1024 native resolution</field>
|
||||
<field name="weight">190.4</field>
|
||||
<field name="price">279.95</field>
|
||||
<field name="popularity">6</field>
|
||||
<field name="inStock">true</field>
|
||||
<!-- Buffalo store -->
|
||||
<field name="store">45.18814,-93.88541</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">EN7800GTX/2DHTV/256M</field>
|
||||
<field name="name">ASUS Extreme N7800GTX/2DHTV (256 MB)</field>
|
||||
<!-- Denormalized -->
|
||||
<field name="manu">ASUS Computer Inc.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">asus</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">graphics card</field>
|
||||
<field name="features">NVIDIA GeForce 7800 GTX GPU/VPU clocked at 486MHz</field>
|
||||
<field name="features">256MB GDDR3 Memory clocked at 1.35GHz</field>
|
||||
<field name="features">PCI Express x16</field>
|
||||
<field name="features">Dual DVI connectors, HDTV out, video input</field>
|
||||
<field name="features">OpenGL 2.0, DirectX 9.0</field>
|
||||
<field name="weight">16.0</field>
|
||||
<field name="price">479.95</field>
|
||||
<field name="popularity">7</field>
|
||||
<field name="store">40.7143,-74.006</field>
|
||||
<field name="inStock">false</field>
|
||||
<field name="manufacturedate_dt">2006-02-13T15:26:37Z/DAY</field>
|
||||
</doc>
|
||||
<!-- yes, you can add more than one document at a time -->
|
||||
<doc>
|
||||
<field name="id">100-435805</field>
|
||||
<field name="name">ATI Radeon X1900 XTX 512 MB PCIE Video Card</field>
|
||||
<field name="manu">ATI Technologies</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">ati</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">graphics card</field>
|
||||
<field name="features">ATI RADEON X1900 GPU/VPU clocked at 650MHz</field>
|
||||
<field name="features">512MB GDDR3 SDRAM clocked at 1.55GHz</field>
|
||||
<field name="features">PCI Express x16</field>
|
||||
<field name="features">dual DVI, HDTV, svideo, composite out</field>
|
||||
<field name="features">OpenGL 2.0, DirectX 9.0</field>
|
||||
<field name="weight">48.0</field>
|
||||
<field name="price">649.99</field>
|
||||
<field name="popularity">7</field>
|
||||
<field name="inStock">false</field>
|
||||
<field name="manufacturedate_dt">2006-02-13T15:26:37Z/DAY</field>
|
||||
<!-- NYC store -->
|
||||
<field name="store">40.7143,-74.006</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">0579B002</field>
|
||||
<field name="name">Canon PIXMA MP500 All-In-One Photo Printer</field>
|
||||
<field name="manu">Canon Inc.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">canon</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">multifunction printer</field>
|
||||
<field name="cat">printer</field>
|
||||
<field name="cat">scanner</field>
|
||||
<field name="cat">copier</field>
|
||||
<field name="features">Multifunction ink-jet color photo printer</field>
|
||||
<field name="features">Flatbed scanner, optical scan resolution of 1,200 x 2,400 dpi</field>
|
||||
<field name="features">2.5" color LCD preview screen</field>
|
||||
<field name="features">Duplex Copying</field>
|
||||
<field name="features">Printing speed up to 29ppm black, 19ppm color</field>
|
||||
<field name="features">Hi-Speed USB</field>
|
||||
<field name="features">memory card: CompactFlash, Micro Drive, SmartMedia, Memory Stick, Memory Stick Pro, SD Card, and MultiMediaCard</field>
|
||||
<field name="weight">352.0</field>
|
||||
<field name="price">179.99</field>
|
||||
<field name="popularity">6</field>
|
||||
<field name="inStock">true</field>
|
||||
<!-- Buffalo store -->
|
||||
<field name="store">45.19214,-93.89941</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">3007WFP</field>
|
||||
<field name="name">Dell Widescreen UltraSharp 3007WFP</field>
|
||||
<field name="manu">Dell, Inc.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">dell</field>
|
||||
<field name="cat">electronics and computer1</field>
|
||||
<field name="features">30" TFT active matrix LCD, 2560 x 1600, .25mm dot pitch, 700:1 contrast</field>
|
||||
<field name="includes">USB cable</field>
|
||||
<field name="weight">401.6</field>
|
||||
<field name="price">2199.0</field>
|
||||
<field name="popularity">6</field>
|
||||
<field name="inStock">true</field>
|
||||
<!-- Buffalo store -->
|
||||
<field name="store">43.17614,-90.57341</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">adata</field>
|
||||
<field name="compName_s">A-Data Technology</field>
|
||||
<field name="address_s">46221 Landing Parkway Fremont, CA 94538</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">apple</field>
|
||||
<field name="compName_s">Apple</field>
|
||||
<field name="address_s">1 Infinite Way, Cupertino CA</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">asus</field>
|
||||
<field name="compName_s">ASUS Computer</field>
|
||||
<field name="address_s">800 Corporate Way Fremont, CA 94539</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">ati</field>
|
||||
<field name="compName_s">ATI Technologies</field>
|
||||
<field name="address_s">33 Commerce Valley Drive East Thornhill, ON L3T 7N6 Canada</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">belkin</field>
|
||||
<field name="compName_s">Belkin</field>
|
||||
<field name="address_s">12045 E. Waterfront Drive Playa Vista, CA 90094</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">canon</field>
|
||||
<field name="compName_s">Canon, Inc.</field>
|
||||
<field name="address_s">One Canon Plaza Lake Success, NY 11042</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">corsair</field>
|
||||
<field name="compName_s">Corsair Microsystems</field>
|
||||
<field name="address_s">46221 Landing Parkway Fremont, CA 94538</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">dell</field>
|
||||
<field name="compName_s">Dell, Inc.</field>
|
||||
<field name="address_s">One Dell Way Round Rock, Texas 78682</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">maxtor</field>
|
||||
<field name="compName_s">Maxtor Corporation</field>
|
||||
<field name="address_s">920 Disc Drive Scotts Valley, CA 95066</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">samsung</field>
|
||||
<field name="compName_s">Samsung Electronics Co. Ltd.</field>
|
||||
<field name="address_s">105 Challenger Rd. Ridgefield Park, NJ 07660-0511</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">viewsonic</field>
|
||||
<field name="compName_s">ViewSonic Corp</field>
|
||||
<field name="address_s">381 Brea Canyon Road Walnut, CA 91789-0708</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">SP2514N</field>
|
||||
<field name="name">Samsung SpinPoint P120 SP2514N - hard drive - 250 GB - ATA-133</field>
|
||||
<field name="manu">Samsung Electronics Co. Ltd.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">samsung</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">hard drive</field>
|
||||
<field name="features">7200RPM, 8MB cache, IDE Ultra ATA-133</field>
|
||||
<field name="features">NoiseGuard, SilentSeek technology, Fluid Dynamic Bearing (FDB) motor</field>
|
||||
<field name="price">92.0</field>
|
||||
<field name="popularity">6</field>
|
||||
<field name="inStock">true</field>
|
||||
<field name="manufacturedate_dt">2006-02-13T15:26:37Z</field>
|
||||
<!-- Near Oklahoma city -->
|
||||
<field name="store">35.0752,-97.032</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">6H500F0</field>
|
||||
<field name="name">Maxtor DiamondMax 11 - hard drive - 500 GB - SATA-300</field>
|
||||
<field name="manu">Maxtor Corp.</field>
|
||||
<!-- Join -->
|
||||
<field name="manu_id_s">maxtor</field>
|
||||
<field name="cat">electronics</field>
|
||||
<field name="cat">hard drive</field>
|
||||
<field name="features">SATA 3.0Gb/s, NCQ</field>
|
||||
<field name="features">8.5ms seek</field>
|
||||
<field name="features">16MB cache</field>
|
||||
<field name="price">350.0</field>
|
||||
<field name="popularity">6</field>
|
||||
<field name="inStock">true</field>
|
||||
<!-- Buffalo store -->
|
||||
<field name="store">45.17614,-93.87341</field>
|
||||
<field name="manufacturedate_dt">2006-02-13T15:26:37Z</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">USD</field>
|
||||
<field name="name">One Dollar</field>
|
||||
<field name="manu">Bank of America</field>
|
||||
<field name="manu_id_s">boa</field>
|
||||
<field name="cat">currency</field>
|
||||
<field name="features">Coins and notes</field>
|
||||
<field name="price_c">1,USD</field>
|
||||
<field name="inStock">true</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">EUR</field>
|
||||
<field name="name">One Euro</field>
|
||||
<field name="manu">European Union</field>
|
||||
<field name="manu_id_s">eu</field>
|
||||
<field name="cat">currency</field>
|
||||
<field name="features">Coins and notes</field>
|
||||
<field name="price_c">1,EUR</field>
|
||||
<field name="inStock">true</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">GBP</field>
|
||||
<field name="name">One British Pound</field>
|
||||
<field name="manu">U.K.</field>
|
||||
<field name="manu_id_s">uk</field>
|
||||
<field name="cat">currency</field>
|
||||
<field name="features">Coins and notes</field>
|
||||
<field name="price_c">1,GBP</field>
|
||||
<field name="inStock">true</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">NOK</field>
|
||||
<field name="name">One Krone</field>
|
||||
<field name="manu">Bank of Norway</field>
|
||||
<field name="manu_id_s">nor</field>
|
||||
<field name="cat">currency</field>
|
||||
<field name="features">Coins and notes</field>
|
||||
<field name="price_c">1,NOK</field>
|
||||
<field name="inStock">true</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">UTF8TEST</field>
|
||||
<field name="name">Test with some UTF-8 encoded characters</field>
|
||||
<field name="manu">Apache Software Foundation</field>
|
||||
<field name="cat">software</field>
|
||||
<field name="cat">search</field>
|
||||
<field name="features">No accents here</field>
|
||||
<field name="features">This is an e acute: é</field>
|
||||
<field name="features">eaiou with circumflexes: êâîôû</field>
|
||||
<field name="features">eaiou with umlauts: ëäïöü</field>
|
||||
<field name="features">tag with escaped chars: <nicetag/></field>
|
||||
<field name="features">escaped ampersand: Bonnie & Clyde</field>
|
||||
<field name="features">Outside the BMP:𐌈 codepoint=10308, a circle with an x inside. UTF8=f0908c88 UTF16=d800 df08</field>
|
||||
<field name="price">0.0</field>
|
||||
<field name="inStock">true</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">GB18030TEST</field>
|
||||
<field name="name">Test with some GB18030 encoded characters</field>
|
||||
<field name="features">No accents here</field>
|
||||
<field name="features">ÕâÊÇÒ»¸ö¹¦ÄÜ</field>
|
||||
<field name="features">This is a feature (translated)</field>
|
||||
<field name="features">Õâ·ÝÎļþÊǺÜÓйâÔó</field>
|
||||
<field name="features">This document is very shiny (translated)</field>
|
||||
<field name="price">0.0</field>
|
||||
<field name="inStock">true</field>
|
||||
</doc>
|
||||
<doc>
|
||||
<field name="id">SOLR1000</field>
|
||||
<field name="name">Solr, the Enterprise Search Server</field>
|
||||
<field name="manu">Apache Software Foundation</field>
|
||||
<field name="cat">software</field>
|
||||
<field name="cat">search</field>
|
||||
<field name="features">Advanced Full-Text Search Capabilities using Lucene</field>
|
||||
<field name="features">Optimized for High Volume Web Traffic</field>
|
||||
<field name="features">Standards Based Open Interfaces - XML and HTTP</field>
|
||||
<field name="features">Comprehensive HTML Administration Interfaces</field>
|
||||
<field name="features">Scalability - Efficient Replication to other Solr Search Servers</field>
|
||||
<field name="features">Flexible and Adaptable with XML configuration and Schema</field>
|
||||
<field name="features">Good unicode support: héllo (hello with an accent over the e)</field>
|
||||
<field name="price">0.0</field>
|
||||
<field name="popularity">10</field>
|
||||
<field name="inStock">true</field>
|
||||
<field name="incubationdate_dt">2006-01-17T00:00:00.000Z</field>
|
||||
</doc>
|
||||
</add>
|
|
@ -27,11 +27,13 @@ import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
|
|||
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
|
||||
import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
|
||||
import org.apache.solr.client.solrj.request.json.JsonQueryRequest;
|
||||
import org.apache.solr.client.solrj.request.json.TermsFacetMap;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.client.solrj.response.UpdateResponse;
|
||||
import org.apache.solr.cloud.SolrCloudTestCase;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.util.ExternalPaths;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
@ -58,7 +60,7 @@ public class JsonRequestApiTest extends SolrCloudTestCase {
|
|||
|
||||
ContentStreamUpdateRequest up = new ContentStreamUpdateRequest("/update");
|
||||
up.setParam("collection", COLLECTION_NAME);
|
||||
up.addFile(getFile("solrj/docs2.xml"), "application/xml"); // A subset of the 'techproducts' documents
|
||||
up.addFile(getFile("solrj/techproducts.xml"), "application/xml");
|
||||
up.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
|
||||
UpdateResponse updateResponse = up.process(cluster.getSolrClient());
|
||||
assertEquals(0, updateResponse.getStatus());
|
||||
|
@ -67,7 +69,7 @@ public class JsonRequestApiTest extends SolrCloudTestCase {
|
|||
@Test
|
||||
public void testSimpleJsonQuery() throws Exception {
|
||||
SolrClient solrClient = cluster.getSolrClient();
|
||||
final int expectedResults = 3;
|
||||
final int expectedResults = 4;
|
||||
|
||||
// tag::solrj-json-query-simple[]
|
||||
final JsonQueryRequest simpleQuery = new JsonQueryRequest()
|
||||
|
@ -116,7 +118,97 @@ public class JsonRequestApiTest extends SolrCloudTestCase {
|
|||
// end::solrj-json-query-macro-expansion[]
|
||||
|
||||
assertEquals(0, queryResponse.getStatus());
|
||||
assertEquals(3, queryResponse.getResults().size());
|
||||
assertEquals(5, queryResponse.getResults().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleJsonTermsFacet() throws Exception {
|
||||
SolrClient solrClient = cluster.getSolrClient();
|
||||
|
||||
//tag::solrj-json-simple-terms-facet[]
|
||||
final TermsFacetMap categoryFacet = new TermsFacetMap("cat").setLimit(3);
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("categories", categoryFacet);
|
||||
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);
|
||||
//end::solrj-json-simple-terms-facet[]
|
||||
|
||||
assertEquals(0, queryResponse.getStatus());
|
||||
assertEquals(32, queryResponse.getResults().getNumFound());
|
||||
assertEquals(10, queryResponse.getResults().size());
|
||||
assertHasFacetWithBucketValues(queryResponse.getResponse(),"categories",
|
||||
new FacetBucket("electronics",12),
|
||||
new FacetBucket("currency", 4),
|
||||
new FacetBucket("memory", 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTermsFacet2() throws Exception {
|
||||
SolrClient solrClient = cluster.getSolrClient();
|
||||
|
||||
//tag::solrj-json-terms-facet2[]
|
||||
final TermsFacetMap categoryFacet = new TermsFacetMap("cat").setLimit(5);
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("categories", categoryFacet);
|
||||
QueryResponse queryResponse = request.process(solrClient, COLLECTION_NAME);
|
||||
//end::solrj-json-terms-facet2[]
|
||||
|
||||
assertEquals(0, queryResponse.getStatus());
|
||||
assertEquals(32, queryResponse.getResults().getNumFound());
|
||||
assertEquals(10, queryResponse.getResults().size());
|
||||
assertHasFacetWithBucketValues(queryResponse.getResponse(),"categories",
|
||||
new FacetBucket("electronics",12),
|
||||
new FacetBucket("currency", 4),
|
||||
new FacetBucket("memory", 3),
|
||||
new FacetBucket("connector", 2),
|
||||
new FacetBucket("graphics card", 2));
|
||||
}
|
||||
|
||||
private class FacetBucket {
|
||||
private final Object val;
|
||||
private final int count;
|
||||
FacetBucket(Object val, int count) {
|
||||
this.val = val;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public Object getVal() { return val; }
|
||||
public int getCount() { return count; }
|
||||
}
|
||||
|
||||
private void assertHasFacetWithBucketValues(NamedList<Object> rawResponse, String expectedFacetName, FacetBucket... expectedBuckets) {
|
||||
final NamedList<Object> facetsTopLevel = assertHasFacetResponse(rawResponse);
|
||||
assertFacetResponseHasFacetWithBuckets(facetsTopLevel, expectedFacetName, expectedBuckets);
|
||||
}
|
||||
|
||||
private NamedList<Object> assertHasFacetResponse(NamedList<Object> topLevelResponse) {
|
||||
Object o = topLevelResponse.get("facets");
|
||||
if (o == null) fail("Response has no top-level 'facets' property as expected");
|
||||
if (!(o instanceof NamedList)) fail("Response has a top-level 'facets' property, but it is not a NamedList");
|
||||
|
||||
return (NamedList<Object>) o;
|
||||
}
|
||||
|
||||
private void assertFacetResponseHasFacetWithBuckets(NamedList<Object> facetResponse, String expectedFacetName, FacetBucket... expectedBuckets) {
|
||||
Object o = facetResponse.get(expectedFacetName);
|
||||
if (o == null) fail("Response has no top-level facet named '" + expectedFacetName + "'");
|
||||
if (!(o instanceof NamedList)) fail("Response has a property for the expected facet '" + expectedFacetName + "' property, but it is not a NamedList");
|
||||
|
||||
final NamedList<Object> expectedFacetTopLevel = (NamedList<Object>) o;
|
||||
o = expectedFacetTopLevel.get("buckets");
|
||||
if (o == null) fail("Response has no 'buckets' property under 'facets'");
|
||||
if (!(o instanceof List)) fail("Response has no 'buckets' property containing actual facet information.");
|
||||
|
||||
final List<NamedList> bucketList = (List<NamedList>) o;
|
||||
assertEquals("Expected " + expectedBuckets.length + " buckets, but found " + bucketList.size(),
|
||||
expectedBuckets.length, bucketList.size());
|
||||
for (int i = 0; i < expectedBuckets.length; i++) {
|
||||
final FacetBucket expectedBucket = expectedBuckets[i];
|
||||
final NamedList<Object> actualBucket = bucketList.get(i);
|
||||
assertEquals(expectedBucket.getVal(), actualBucket.get("val"));
|
||||
assertEquals(expectedBucket.getCount(), actualBucket.get("count"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,615 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
|
||||
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
|
||||
import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.client.solrj.response.UpdateResponse;
|
||||
import org.apache.solr.cloud.SolrCloudTestCase;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.util.ExternalPaths;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DirectJsonQueryRequestFacetingIntegrationTest extends SolrCloudTestCase {
|
||||
|
||||
private static final String COLLECTION_NAME = "techproducts";
|
||||
private static final String CONFIG_NAME = "techproducts_config";
|
||||
private static final int NUM_TECHPRODUCTS_DOCS = 32;
|
||||
private static final int NUM_IN_STOCK = 17;
|
||||
private static final int NUM_ELECTRONICS = 12;
|
||||
private static final int NUM_CURRENCY = 4;
|
||||
private static final int NUM_MEMORY = 3;
|
||||
private static final int NUM_CORSAIR = 3;
|
||||
private static final int NUM_BELKIN = 2;
|
||||
private static final int NUM_CANON = 2;
|
||||
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void setupCluster() throws Exception {
|
||||
configureCluster(1)
|
||||
.addConfig(CONFIG_NAME, new File(ExternalPaths.TECHPRODUCTS_CONFIGSET).toPath())
|
||||
.configure();
|
||||
|
||||
final List<String> solrUrls = new ArrayList<>();
|
||||
solrUrls.add(cluster.getJettySolrRunner(0).getBaseUrl().toString());
|
||||
|
||||
CollectionAdminRequest.createCollection(COLLECTION_NAME, CONFIG_NAME, 1, 1).process(cluster.getSolrClient());
|
||||
|
||||
ContentStreamUpdateRequest up = new ContentStreamUpdateRequest("/update");
|
||||
up.setParam("collection", COLLECTION_NAME);
|
||||
up.addFile(getFile("solrj/techproducts.xml"), "application/xml");
|
||||
up.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
|
||||
UpdateResponse updateResponse = up.process(cluster.getSolrClient());
|
||||
assertEquals(0, updateResponse.getStatus());
|
||||
}
|
||||
@Test
|
||||
public void testSingleTermsFacet() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'top_cats': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'limit': 3",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats", new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY), new FacetBucket("memory", NUM_MEMORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiTermsFacet() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'top_cats': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'limit': 3",
|
||||
" },",
|
||||
" 'top_manufacturers': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'manu_id_s',",
|
||||
" 'limit': 3",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats", new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY), new FacetBucket("memory", NUM_MEMORY));
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_manufacturers", new FacetBucket("corsair",NUM_CORSAIR),
|
||||
new FacetBucket("belkin", NUM_BELKIN), new FacetBucket("canon", NUM_CANON));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleRangeFacet() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'prices': {",
|
||||
" 'type': 'range',",
|
||||
" 'field': 'price',",
|
||||
" 'start': 0,",
|
||||
" 'end': 100,",
|
||||
" 'gap': 20",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"prices",
|
||||
new FacetBucket(0.0f, 5),
|
||||
new FacetBucket(20.0f, 0),
|
||||
new FacetBucket(40.0f, 0),
|
||||
new FacetBucket(60.0f, 1),
|
||||
new FacetBucket(80.0f, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiRangeFacet() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'prices': {",
|
||||
" 'type': 'range',",
|
||||
" 'field': 'price',",
|
||||
" 'start': 0,",
|
||||
" 'end': 100,",
|
||||
" 'gap': 20",
|
||||
" },",
|
||||
" 'shipping_weights': {",
|
||||
" 'type': 'range',",
|
||||
" 'field': 'weight',",
|
||||
" 'start': 0,",
|
||||
" 'end': 200,",
|
||||
" 'gap': 50",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"prices",
|
||||
new FacetBucket(0.0f, 5),
|
||||
new FacetBucket(20.0f, 0),
|
||||
new FacetBucket(40.0f, 0),
|
||||
new FacetBucket(60.0f, 1),
|
||||
new FacetBucket(80.0f, 1));
|
||||
assertHasFacetWithBucketValues(rawResponse, "shipping_weights",
|
||||
new FacetBucket(0.0f, 6),
|
||||
new FacetBucket(50.0f, 0),
|
||||
new FacetBucket(100.0f, 0),
|
||||
new FacetBucket(150.0f,1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleStatFacet() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'sum_price': 'sum(price)'",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasStatFacetWithValue(rawResponse,"sum_price", 5251.270030975342);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiStatFacet() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'sum_price': 'sum(price)',",
|
||||
" 'avg_price': 'avg(price)'",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasStatFacetWithValue(rawResponse,"sum_price", 5251.270030975342);
|
||||
assertHasStatFacetWithValue(rawResponse,"avg_price", 328.20437693595886);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiFacetsMixedTypes() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'avg_price': 'avg(price)',",
|
||||
" 'top_cats': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'limit': 3",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasStatFacetWithValue(rawResponse,"avg_price", 328.20437693595886);
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats", new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY), new FacetBucket("memory", NUM_MEMORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedTermsFacet() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'top_cats': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'limit': 3",
|
||||
" 'facet': {",
|
||||
" 'top_manufacturers_for_cat': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'manu_id_s',",
|
||||
" 'limit': 1",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
|
||||
// Test top level facets
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats", new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY), new FacetBucket("memory", NUM_MEMORY));
|
||||
|
||||
// Test subfacet values for each top-level facet bucket
|
||||
final List<NamedList<Object>> topLevelFacetResponse = (List<NamedList<Object>>) rawResponse.findRecursive("facets", "top_cats", "buckets");
|
||||
final NamedList<Object> electronicsSubFacet = topLevelFacetResponse.get(0);
|
||||
assertFacetResponseHasFacetWithBuckets(electronicsSubFacet, "top_manufacturers_for_cat", new FacetBucket("corsair", 3));
|
||||
final NamedList<Object> currencySubfacet = topLevelFacetResponse.get(1);
|
||||
assertFacetResponseHasFacetWithBuckets(currencySubfacet, "top_manufacturers_for_cat", new FacetBucket("boa", 1));
|
||||
final NamedList<Object> memorySubfacet = topLevelFacetResponse.get(2);
|
||||
assertFacetResponseHasFacetWithBuckets(memorySubfacet, "top_manufacturers_for_cat", new FacetBucket("corsair", 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedFacetsOfMixedTypes() throws Exception {
|
||||
final String subfacetName = "avg_price_for_cat";
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'top_cats': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'limit': 3",
|
||||
" 'facet': {",
|
||||
" 'avg_price_for_cat': 'avg(price)'",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
|
||||
// Test top level facets
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats", new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY), new FacetBucket("memory", NUM_MEMORY));
|
||||
|
||||
// Test subfacet values for each top-level facet bucket
|
||||
final List<NamedList<Object>> topLevelFacetResponse = (List<NamedList<Object>>) rawResponse.findRecursive("facets", "top_cats", "buckets");
|
||||
final NamedList<Object> electronicsSubFacet = topLevelFacetResponse.get(0);
|
||||
assertFacetResponseHasStatFacetWithValue(electronicsSubFacet, subfacetName, 252.02909261530095);
|
||||
final NamedList<Object> currencySubfacet = topLevelFacetResponse.get(1);
|
||||
assertFacetResponseHasStatFacetWithValue(currencySubfacet, subfacetName, 0.0);
|
||||
final NamedList<Object> memorySubfacet = topLevelFacetResponse.get(2);
|
||||
assertFacetResponseHasStatFacetWithValue(memorySubfacet, subfacetName, 129.99499893188477);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetWithDomainFilteredBySimpleQueryString() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'top_popular_cats': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'limit': 3",
|
||||
" 'domain': {",
|
||||
" 'filter': 'popularity:[5 TO 10]'",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
|
||||
// Test top level facets
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_popular_cats", new FacetBucket("electronics",9),
|
||||
new FacetBucket("graphics card", 2), new FacetBucket("hard drive", 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetWithDomainFilteredByLocalParamsQueryString() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'facet': {",
|
||||
" 'top_popular_cats': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'limit': 3",
|
||||
" 'domain': {",
|
||||
" 'filter': '{!lucene df=\"popularity\" v=\"[5 TO 10]\"}'",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
|
||||
// Test top level facets
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_popular_cats", new FacetBucket("electronics",9),
|
||||
new FacetBucket("graphics card", 2), new FacetBucket("hard drive", 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetWithArbitraryDomainFromQueryString() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': 'cat:electronics',",
|
||||
" 'facet': {",
|
||||
" 'top_cats': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'limit': 3",
|
||||
" 'domain': {",
|
||||
" 'query': '*:*'",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_ELECTRONICS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
|
||||
// Test top level facets
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats", new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY), new FacetBucket("memory", NUM_MEMORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetWithArbitraryDomainFromLocalParamsQuery() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': 'cat:electronics',",
|
||||
" 'facet': {",
|
||||
" 'largest_search_cats': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'domain': {",
|
||||
" 'query': '{!lucene df=\"cat\" v=\"search\"}'",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_ELECTRONICS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
|
||||
assertHasFacetWithBucketValues(rawResponse,"largest_search_cats",
|
||||
new FacetBucket("search",2),
|
||||
new FacetBucket("software", 2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Multiple query clauses are effectively AND'd together
|
||||
*/
|
||||
public void testFacetWithMultipleSimpleQueryClausesInArbitraryDomain() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': 'cat:electronics',",
|
||||
" 'facet': {",
|
||||
" 'cats_matching_solr': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'domain': {",
|
||||
" 'query': ['cat:search', 'name:Solr']",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_ELECTRONICS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
|
||||
assertHasFacetWithBucketValues(rawResponse,"cats_matching_solr",
|
||||
new FacetBucket("search",1),
|
||||
new FacetBucket("software", 1));
|
||||
}
|
||||
|
||||
public void testFacetWithMultipleLocalParamsQueryClausesInArbitraryDomain() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': 'cat:electronics',",
|
||||
" 'facet': {",
|
||||
" 'cats_matching_solr': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'domain': {",
|
||||
" 'query': ['{!lucene df=\"cat\" v=\"search\"}', '{!lucene df=\"name\" v=\"Solr\"}']",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_ELECTRONICS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
|
||||
// Test top level facets
|
||||
assertHasFacetWithBucketValues(rawResponse,"cats_matching_solr",
|
||||
new FacetBucket("search",1),
|
||||
new FacetBucket("software", 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetWithDomainWidenedUsingExcludeTagsToIgnoreFilters() throws Exception {
|
||||
final String jsonBody = String.join("\n","{",
|
||||
" 'query': '*:*',",
|
||||
" 'filter': {'#on_shelf': 'inStock:true'},",
|
||||
" 'facet': {",
|
||||
" 'in_stock_only': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'limit': 2",
|
||||
" }",
|
||||
" 'all': {",
|
||||
" 'type': 'terms',",
|
||||
" 'field': 'cat',",
|
||||
" 'limit': 2,",
|
||||
" 'domain': {",
|
||||
" 'excludeTags': 'on_shelf'",
|
||||
" }",
|
||||
" }",
|
||||
" }",
|
||||
"}");
|
||||
final DirectJsonQueryRequest request = new DirectJsonQueryRequest(jsonBody);
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_IN_STOCK, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
|
||||
assertHasFacetWithBucketValues(rawResponse,"in_stock_only",
|
||||
new FacetBucket("electronics",8),
|
||||
new FacetBucket("currency", 4));
|
||||
assertHasFacetWithBucketValues(rawResponse,"all",
|
||||
new FacetBucket("electronics",12),
|
||||
new FacetBucket("currency", 4));
|
||||
}
|
||||
|
||||
private class FacetBucket {
|
||||
private final Object val;
|
||||
private final int count;
|
||||
FacetBucket(Object val, int count) {
|
||||
this.val = val;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public Object getVal() { return val; }
|
||||
public int getCount() { return count; }
|
||||
}
|
||||
|
||||
private void assertHasFacetWithBucketValues(NamedList<Object> rawResponse, String expectedFacetName, FacetBucket... expectedBuckets) {
|
||||
final NamedList<Object> facetsTopLevel = assertHasFacetResponse(rawResponse);
|
||||
assertFacetResponseHasFacetWithBuckets(facetsTopLevel, expectedFacetName, expectedBuckets);
|
||||
}
|
||||
|
||||
private void assertHasStatFacetWithValue(NamedList<Object> rawResponse, String expectedFacetName, Double expectedStatValue) {
|
||||
final NamedList<Object> facetsTopLevel = assertHasFacetResponse(rawResponse);
|
||||
assertFacetResponseHasStatFacetWithValue(facetsTopLevel, expectedFacetName, expectedStatValue);
|
||||
}
|
||||
|
||||
private NamedList<Object> assertHasFacetResponse(NamedList<Object> topLevelResponse) {
|
||||
Object o = topLevelResponse.get("facets");
|
||||
if (o == null) fail("Response has no top-level 'facets' property as expected");
|
||||
if (!(o instanceof NamedList)) fail("Response has a top-level 'facets' property, but it is not a NamedList");
|
||||
|
||||
return (NamedList<Object>) o;
|
||||
}
|
||||
|
||||
private void assertFacetResponseHasFacetWithBuckets(NamedList<Object> facetResponse, String expectedFacetName, FacetBucket... expectedBuckets) {
|
||||
Object o = facetResponse.get(expectedFacetName);
|
||||
if (o == null) fail("Response has no top-level facet named '" + expectedFacetName + "'");
|
||||
if (!(o instanceof NamedList)) fail("Response has a property for the expected facet '" + expectedFacetName + "' property, but it is not a NamedList");
|
||||
|
||||
final NamedList<Object> expectedFacetTopLevel = (NamedList<Object>) o;
|
||||
o = expectedFacetTopLevel.get("buckets");
|
||||
if (o == null) fail("Response has no 'buckets' property under 'facets'");
|
||||
if (!(o instanceof List)) fail("Response has no 'buckets' property containing actual facet information.");
|
||||
|
||||
final List<NamedList> bucketList = (List<NamedList>) o;
|
||||
assertEquals("Expected " + expectedBuckets.length + " buckets, but found " + bucketList.size(),
|
||||
expectedBuckets.length, bucketList.size());
|
||||
for (int i = 0; i < expectedBuckets.length; i++) {
|
||||
final FacetBucket expectedBucket = expectedBuckets[i];
|
||||
final NamedList<Object> actualBucket = bucketList.get(i);
|
||||
assertEquals(expectedBucket.getVal(), actualBucket.get("val"));
|
||||
assertEquals(expectedBucket.getCount(), actualBucket.get("count"));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFacetResponseHasStatFacetWithValue(NamedList<Object> facetResponse, String expectedFacetName, Double expectedStatValue) {
|
||||
Object o = facetResponse.get(expectedFacetName);
|
||||
if (o == null) fail("Response has no top-level facet named '" + expectedFacetName + "'");
|
||||
if (!(o instanceof Number)) fail("Response has a property for the expected facet '" + expectedFacetName + "' property, but it is not a Number");
|
||||
|
||||
final Number actualStatValueAsNumber = (Number) o;
|
||||
final Double actualStatValueAsDouble = ((Number) o).doubleValue();
|
||||
assertEquals(expectedStatValue, actualStatValueAsDouble, 0.5);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.internal.matchers.StringContains.containsString;
|
||||
|
||||
public class DomainMapTest extends SolrTestCaseJ4 {
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidFilters() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new DomainMap()
|
||||
.withFilter(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresFilterWithCorrectKey() {
|
||||
final DomainMap domain = new DomainMap()
|
||||
.withFilter("name:Solr");
|
||||
final List<String> filterList = (List<String>) domain.get("filter");
|
||||
|
||||
assertTrue("Expected filter list to contain provided filter", filterList.contains("name:Solr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresMultipleFilters() {
|
||||
final DomainMap domain = new DomainMap()
|
||||
.withFilter("name:Solr")
|
||||
.withFilter("cat:search");
|
||||
final List<String> filterList = (List<String>) domain.get("filter");
|
||||
|
||||
assertTrue("Expected filter list to contain 1st provided filter", filterList.contains("name:Solr"));
|
||||
assertTrue("Expected filter list to contain 2nd provided filter", filterList.contains("cat:search"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidQueries() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new DomainMap()
|
||||
.withQuery(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresQueryWithCorrectKey() {
|
||||
final DomainMap domain = new DomainMap()
|
||||
.withQuery("name:Solr");
|
||||
final List<String> queryList = (List<String>) domain.get("query");
|
||||
|
||||
assertTrue("Expected query list to contain provided query", queryList.contains("name:Solr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresMultipleQueries() {
|
||||
final DomainMap domain = new DomainMap()
|
||||
.withQuery("name:Solr")
|
||||
.withQuery("cat:search");
|
||||
final List<String> queryList = (List<String>) domain.get("query");
|
||||
|
||||
assertTrue("Expected query list to contain 1st provided query", queryList.contains("name:Solr"));
|
||||
assertTrue("Expected query list to contain 2nd provided query", queryList.contains("cat:search"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidTagsToExclude() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new DomainMap()
|
||||
.withTagsToExclude(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresTagsToExcludeWithCorrectKey() {
|
||||
final DomainMap domain = new DomainMap()
|
||||
.withTagsToExclude("BRAND");
|
||||
final List<String> exclusionList = (List<String>) domain.get("excludeTags");
|
||||
|
||||
assertTrue("Expected tag-exclusion list to contain provided tag", exclusionList.contains("BRAND"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresMultipleTagExclusionStrings() {
|
||||
final DomainMap domain = new DomainMap()
|
||||
.withTagsToExclude("BRAND")
|
||||
.withTagsToExclude("COLOR");
|
||||
final List<String> exclusionList = (List<String>) domain.get("excludeTags");
|
||||
|
||||
assertTrue("Expected tag-exclusion list to contain provided 1st tag", exclusionList.contains("BRAND"));
|
||||
assertTrue("Expected tag-exclusion list to contain provided 2nd tag", exclusionList.contains("COLOR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidBlockParentQuery() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new DomainMap()
|
||||
.setBlockParentQuery(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresBlockParentQueryWithCorrectKey() {
|
||||
final DomainMap domain = new DomainMap()
|
||||
.setBlockParentQuery("content_type:product");
|
||||
assertEquals("content_type:product", domain.get("blockParent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidBlockChildrenQuery() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new DomainMap()
|
||||
.setBlockChildQuery(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresBlockChildrenQueryWithCorrectKey() {
|
||||
final DomainMap domain = new DomainMap()
|
||||
.setBlockChildQuery("content_type:productColors");
|
||||
assertEquals("content_type:productColors", domain.get("blockChildren"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidJoinFromParam() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new DomainMap()
|
||||
.setJoinTransformation(null, "valid-to-field");
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidJoinToParam() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new DomainMap()
|
||||
.setJoinTransformation("valid-from-field", null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresJoinValuesWithCorrectKey() {
|
||||
final DomainMap domain = new DomainMap()
|
||||
.setJoinTransformation("any-from-field", "any-to-field");
|
||||
|
||||
assertTrue(domain.containsKey("join"));
|
||||
final Map<String, Object> joinParams = (Map<String, Object>) domain.get("join");
|
||||
assertEquals("any-from-field", joinParams.get("from"));
|
||||
assertEquals("any-to-field", joinParams.get("to"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.internal.matchers.StringContains.containsString;
|
||||
|
||||
|
||||
public class HeatmapFacetMapTest extends SolrTestCaseJ4 {
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidFieldName() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new HeatmapFacetMap(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresFieldNameWithCorrectKey() {
|
||||
final HeatmapFacetMap heatmapFacet = new HeatmapFacetMap("ANY_FIELD_NAME");
|
||||
assertEquals("ANY_FIELD_NAME", heatmapFacet.get("field"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesntSupportSubfacets() {
|
||||
final Throwable thrown = expectThrows(UnsupportedOperationException.class, () -> {
|
||||
new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.withSubFacet("ANY_NAME", new TermsFacetMap("ANY_OTHER_FIELD_NAME"));
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("doesn't currently support subfacets"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidRegionQueries() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.setRegionQuery(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresRegionQueryWithCorrectKey() {
|
||||
final HeatmapFacetMap heatmapFacet = new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.setRegionQuery("[-120,-35 TO 50,60]");
|
||||
assertEquals("[-120,-35 TO 50,60]", heatmapFacet.get("geom"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidCellSize() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.setGridLevel(0);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be a positive integer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresCellSizeWithCorrectKey() {
|
||||
final HeatmapFacetMap heatmapFacet = new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.setGridLevel(42);
|
||||
assertEquals(42, heatmapFacet.get("gridLevel"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidDistanceError() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.setDistErr(-1.0);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-negative"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresDistanceErrorWithCorrectKey() {
|
||||
final HeatmapFacetMap heatmapFacet = new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.setDistErr(4.5);
|
||||
assertEquals(4.5, heatmapFacet.get("distErr"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidDistanceErrorPercentageWithCorrectKey() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.setDistErrPct(2.0);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be between 0.0 and 1.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresDistanceErrorPercentageWithCorrectKey() {
|
||||
final HeatmapFacetMap heatmapFacet = new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.setDistErrPct(0.45);
|
||||
assertEquals(0.45, heatmapFacet.get("distErrPct"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidHeatmapFormat() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.setHeatmapFormat(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresHeatmapFormatWithCorrectKey() {
|
||||
final HeatmapFacetMap heatmapFacet = new HeatmapFacetMap("ANY_FIELD_NAME")
|
||||
.setHeatmapFormat(HeatmapFacetMap.HeatmapFormat.PNG);
|
||||
assertEquals("png", heatmapFacet.get("format"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,530 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
|
||||
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
|
||||
import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.client.solrj.response.UpdateResponse;
|
||||
import org.apache.solr.cloud.SolrCloudTestCase;
|
||||
import org.apache.solr.common.MapWriter;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.util.ExternalPaths;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JsonQueryRequestFacetingIntegrationTest extends SolrCloudTestCase {
|
||||
|
||||
private static final String COLLECTION_NAME = "techproducts";
|
||||
private static final String CONFIG_NAME = "techproducts_config";
|
||||
private static final int NUM_TECHPRODUCTS_DOCS = 32;
|
||||
private static final int NUM_IN_STOCK = 17;
|
||||
private static final int NUM_ELECTRONICS = 12;
|
||||
private static final int NUM_CURRENCY = 4;
|
||||
private static final int NUM_MEMORY = 3;
|
||||
private static final int NUM_CORSAIR = 3;
|
||||
private static final int NUM_BELKIN = 2;
|
||||
private static final int NUM_CANON = 2;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupCluster() throws Exception {
|
||||
configureCluster(1)
|
||||
.addConfig(CONFIG_NAME, new File(ExternalPaths.TECHPRODUCTS_CONFIGSET).toPath())
|
||||
.configure();
|
||||
|
||||
final List<String> solrUrls = new ArrayList<>();
|
||||
solrUrls.add(cluster.getJettySolrRunner(0).getBaseUrl().toString());
|
||||
|
||||
CollectionAdminRequest.createCollection(COLLECTION_NAME, CONFIG_NAME, 1, 1).process(cluster.getSolrClient());
|
||||
|
||||
ContentStreamUpdateRequest up = new ContentStreamUpdateRequest("/update");
|
||||
up.setParam("collection", COLLECTION_NAME);
|
||||
up.addFile(getFile("solrj/techproducts.xml"), "application/xml");
|
||||
up.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
|
||||
UpdateResponse updateResponse = up.process(cluster.getSolrClient());
|
||||
assertEquals(0, updateResponse.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleTermsFacet() throws Exception {
|
||||
final TermsFacetMap categoriesFacetMap = new TermsFacetMap("cat")
|
||||
.setLimit(3);
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("top_cats", categoriesFacetMap);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats",
|
||||
new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY),
|
||||
new FacetBucket("memory", NUM_MEMORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetCanBeRepresentedByMapWriter() throws Exception {
|
||||
final MapWriter categoriesFacet = new MapWriter() {
|
||||
@Override
|
||||
public void writeMap(EntryWriter ew) throws IOException {
|
||||
ew.put("type", "terms");
|
||||
ew.put("field", "cat");
|
||||
ew.put("limit", 3);
|
||||
}
|
||||
};
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("top_cats", categoriesFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats",
|
||||
new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY),
|
||||
new FacetBucket("memory", NUM_MEMORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiTermsFacet() throws Exception {
|
||||
final TermsFacetMap categoriesFacetMap = new TermsFacetMap("cat")
|
||||
.setLimit(3);
|
||||
final TermsFacetMap manufacturersFacetMap = new TermsFacetMap("manu_id_s")
|
||||
.setLimit(3);
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("top_cats", categoriesFacetMap)
|
||||
.withFacet("top_manufacturers", manufacturersFacetMap);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats",
|
||||
new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY),
|
||||
new FacetBucket("memory", NUM_MEMORY));
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_manufacturers",
|
||||
new FacetBucket("corsair",NUM_CORSAIR),
|
||||
new FacetBucket("belkin", NUM_BELKIN),
|
||||
new FacetBucket("canon", NUM_CANON));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleRangeFacet() throws Exception {
|
||||
final RangeFacetMap pricesFacet = new RangeFacetMap("price", 0, 100, 20);
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("prices", pricesFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"prices",
|
||||
new FacetBucket(0.0f, 5),
|
||||
new FacetBucket(20.0f, 0),
|
||||
new FacetBucket(40.0f, 0),
|
||||
new FacetBucket(60.0f, 1),
|
||||
new FacetBucket(80.0f, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiRangeFacet() throws Exception {
|
||||
final RangeFacetMap pricesFacet = new RangeFacetMap("price", 0, 100, 20);
|
||||
final RangeFacetMap shippingWeightFacet = new RangeFacetMap("weight", 0, 200, 50);
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("prices", pricesFacet)
|
||||
.withFacet("shipping_weights", shippingWeightFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"prices",
|
||||
new FacetBucket(0.0f, 5),
|
||||
new FacetBucket(20.0f, 0),
|
||||
new FacetBucket(40.0f, 0),
|
||||
new FacetBucket(60.0f, 1),
|
||||
new FacetBucket(80.0f, 1));
|
||||
assertHasFacetWithBucketValues(rawResponse, "shipping_weights",
|
||||
new FacetBucket(0.0f, 6),
|
||||
new FacetBucket(50.0f, 0),
|
||||
new FacetBucket(100.0f, 0),
|
||||
new FacetBucket(150.0f,1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleStatFacet() throws Exception {
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withStatFacet("sum_price", "sum(price)");
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasStatFacetWithValue(rawResponse,"sum_price", 5251.270030975342);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiStatFacet() throws Exception {
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withStatFacet("sum_price", "sum(price)")
|
||||
.withStatFacet("avg_price", "avg(price)");
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasStatFacetWithValue(rawResponse,"sum_price", 5251.270030975342);
|
||||
assertHasStatFacetWithValue(rawResponse,"avg_price", 328.20437693595886);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiFacetsMixedTypes() throws Exception {
|
||||
final TermsFacetMap categoryFacet = new TermsFacetMap("cat")
|
||||
.setLimit(3);
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withStatFacet("avg_price", "avg(price)")
|
||||
.withFacet("top_cats", categoryFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasStatFacetWithValue(rawResponse,"avg_price", 328.20437693595886);
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats", new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY), new FacetBucket("memory", NUM_MEMORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedTermsFacet() throws Exception {
|
||||
final TermsFacetMap categoriesFacet = new TermsFacetMap("cat")
|
||||
.setLimit(3)
|
||||
.withSubFacet("top_manufacturers_for_cat", new TermsFacetMap("manu_id_s").setLimit(1));
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("top_cats", categoriesFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
// Test top level facets
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats", new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY), new FacetBucket("memory", NUM_MEMORY));
|
||||
// Test subfacet values for each top-level facet bucket
|
||||
final List<NamedList<Object>> topLevelFacetResponse = (List<NamedList<Object>>) rawResponse.findRecursive("facets", "top_cats", "buckets");
|
||||
final NamedList<Object> electronicsSubFacet = topLevelFacetResponse.get(0);
|
||||
assertFacetResponseHasFacetWithBuckets(electronicsSubFacet, "top_manufacturers_for_cat", new FacetBucket("corsair", 3));
|
||||
final NamedList<Object> currencySubfacet = topLevelFacetResponse.get(1);
|
||||
assertFacetResponseHasFacetWithBuckets(currencySubfacet, "top_manufacturers_for_cat", new FacetBucket("boa", 1));
|
||||
final NamedList<Object> memorySubfacet = topLevelFacetResponse.get(2);
|
||||
assertFacetResponseHasFacetWithBuckets(memorySubfacet, "top_manufacturers_for_cat", new FacetBucket("corsair", 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedFacetsOfMixedTypes() throws Exception {
|
||||
final String subfacetName = "avg_price_for_cat";
|
||||
|
||||
final TermsFacetMap categoriesFacet = new TermsFacetMap("cat")
|
||||
.setLimit(3)
|
||||
.withStatSubFacet(subfacetName, "avg(price)");
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("top_cats", categoriesFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
// Test top level facets
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats", new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY), new FacetBucket("memory", NUM_MEMORY));
|
||||
// Test subfacet values for each top-level facet bucket
|
||||
final List<NamedList<Object>> topLevelFacetResponse = (List<NamedList<Object>>) rawResponse.findRecursive("facets", "top_cats", "buckets");
|
||||
final NamedList<Object> electronicsSubFacet = topLevelFacetResponse.get(0);
|
||||
assertFacetResponseHasStatFacetWithValue(electronicsSubFacet, subfacetName, 252.02909261530095);
|
||||
final NamedList<Object> currencySubfacet = topLevelFacetResponse.get(1);
|
||||
assertFacetResponseHasStatFacetWithValue(currencySubfacet, subfacetName, 0.0);
|
||||
final NamedList<Object> memorySubfacet = topLevelFacetResponse.get(2);
|
||||
assertFacetResponseHasStatFacetWithValue(memorySubfacet, subfacetName, 129.99499893188477);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetWithDomainFilteredBySimpleQueryString() throws Exception {
|
||||
final TermsFacetMap popularCategoriesFacet = new TermsFacetMap("cat")
|
||||
.setLimit(3)
|
||||
.withDomain(new DomainMap()
|
||||
.withFilter("popularity:[5 TO 10]"));
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("top_popular_cats", popularCategoriesFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_popular_cats", new FacetBucket("electronics",9),
|
||||
new FacetBucket("graphics card", 2), new FacetBucket("hard drive", 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetWithDomainFilteredByLocalParamsQueryString() throws Exception {
|
||||
final TermsFacetMap popularCategoriesFacet = new TermsFacetMap("cat")
|
||||
.setLimit(3)
|
||||
.withDomain(new DomainMap()
|
||||
.withFilter("{!lucene df=\"popularity\" v=\"[5 TO 10]\"}"));
|
||||
|
||||
JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFacet("top_popular_cats", popularCategoriesFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_TECHPRODUCTS_DOCS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_popular_cats", new FacetBucket("electronics",9),
|
||||
new FacetBucket("graphics card", 2), new FacetBucket("hard drive", 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetWithArbitraryDomainFromQueryString() throws Exception {
|
||||
final TermsFacetMap categoriesFacet = new TermsFacetMap("cat")
|
||||
.setLimit(3)
|
||||
.withDomain(new DomainMap()
|
||||
.withQuery("*:*"));
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("cat:electronics")
|
||||
.withFacet("top_cats", categoriesFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_ELECTRONICS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"top_cats", new FacetBucket("electronics",NUM_ELECTRONICS),
|
||||
new FacetBucket("currency", NUM_CURRENCY), new FacetBucket("memory", NUM_MEMORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetWithArbitraryDomainFromLocalParamsQuery() throws Exception {
|
||||
final TermsFacetMap searchCategoriesFacet = new TermsFacetMap("cat")
|
||||
.withDomain(new DomainMap()
|
||||
.withQuery("{!lucene df=\"cat\" v=\"search\"}"));
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("cat:electronics")
|
||||
.withFacet("largest_search_cats", searchCategoriesFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_ELECTRONICS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"largest_search_cats",
|
||||
new FacetBucket("search",2),
|
||||
new FacetBucket("software", 2));
|
||||
}
|
||||
|
||||
public void testFacetWithMultipleSimpleQueryClausesInArbitraryDomain() throws Exception {
|
||||
final TermsFacetMap solrCategoriesFacet = new TermsFacetMap("cat")
|
||||
.withDomain(new DomainMap()
|
||||
.withQuery("cat:search")
|
||||
.withQuery("name:Solr"));
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("cat:electronics")
|
||||
.withFacet("cats_matching_solr", solrCategoriesFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_ELECTRONICS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"cats_matching_solr",
|
||||
new FacetBucket("search",1),
|
||||
new FacetBucket("software", 1));
|
||||
}
|
||||
|
||||
public void testFacetWithMultipleLocalParamsQueryClausesInArbitraryDomain() throws Exception {
|
||||
final TermsFacetMap solrCategoriesFacet = new TermsFacetMap("cat")
|
||||
.withDomain(new DomainMap()
|
||||
.withQuery("{!lucene df=\"cat\" v=\"search\"}")
|
||||
.withQuery("{!lucene df=\"name\" v=\"Solr\"}"));
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("cat:electronics")
|
||||
.withFacet("cats_matching_solr", solrCategoriesFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_ELECTRONICS, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"cats_matching_solr",
|
||||
new FacetBucket("search",1),
|
||||
new FacetBucket("software", 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFacetWithDomainWidenedUsingExcludeTagsToIgnoreFilters() throws Exception {
|
||||
final TermsFacetMap inStockFacet = new TermsFacetMap("cat")
|
||||
.setLimit(2);
|
||||
final TermsFacetMap allProductsFacet = new TermsFacetMap("cat")
|
||||
.setLimit(2).withDomain(new DomainMap().withTagsToExclude("on_shelf"));
|
||||
final Map<String, Object> taggedFilterMap = new HashMap<>();
|
||||
taggedFilterMap.put("#on_shelf", "inStock:true");
|
||||
final JsonQueryRequest request = new JsonQueryRequest()
|
||||
.setQuery("*:*")
|
||||
.withFilter(taggedFilterMap)
|
||||
.withFacet("in_stock_only", inStockFacet)
|
||||
.withFacet("all", allProductsFacet);
|
||||
|
||||
QueryResponse response = request.process(cluster.getSolrClient(), COLLECTION_NAME);
|
||||
|
||||
assertEquals(0, response.getStatus());
|
||||
final SolrDocumentList returnedDocs = response.getResults();
|
||||
assertEquals(NUM_IN_STOCK, returnedDocs.getNumFound());
|
||||
assertEquals(10, returnedDocs.size());
|
||||
final NamedList<Object> rawResponse = response.getResponse();
|
||||
assertHasFacetWithBucketValues(rawResponse,"in_stock_only",
|
||||
new FacetBucket("electronics",8),
|
||||
new FacetBucket("currency", 4));
|
||||
assertHasFacetWithBucketValues(rawResponse,"all",
|
||||
new FacetBucket("electronics",12),
|
||||
new FacetBucket("currency", 4));
|
||||
}
|
||||
|
||||
private class FacetBucket {
|
||||
private final Object val;
|
||||
private final int count;
|
||||
FacetBucket(Object val, int count) {
|
||||
this.val = val;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public Object getVal() { return val; }
|
||||
public int getCount() { return count; }
|
||||
}
|
||||
|
||||
private void assertHasFacetWithBucketValues(NamedList<Object> rawResponse, String expectedFacetName, FacetBucket... expectedBuckets) {
|
||||
final NamedList<Object> facetsTopLevel = assertHasFacetResponse(rawResponse);
|
||||
assertFacetResponseHasFacetWithBuckets(facetsTopLevel, expectedFacetName, expectedBuckets);
|
||||
}
|
||||
|
||||
private void assertHasStatFacetWithValue(NamedList<Object> rawResponse, String expectedFacetName, Double expectedStatValue) {
|
||||
final NamedList<Object> facetsTopLevel = assertHasFacetResponse(rawResponse);
|
||||
assertFacetResponseHasStatFacetWithValue(facetsTopLevel, expectedFacetName, expectedStatValue);
|
||||
}
|
||||
|
||||
private NamedList<Object> assertHasFacetResponse(NamedList<Object> topLevelResponse) {
|
||||
Object o = topLevelResponse.get("facets");
|
||||
if (o == null) fail("Response has no top-level 'facets' property as expected");
|
||||
if (!(o instanceof NamedList)) fail("Response has a top-level 'facets' property, but it is not a NamedList");
|
||||
|
||||
return (NamedList<Object>) o;
|
||||
}
|
||||
|
||||
private void assertFacetResponseHasFacetWithBuckets(NamedList<Object> facetResponse, String expectedFacetName, FacetBucket... expectedBuckets) {
|
||||
Object o = facetResponse.get(expectedFacetName);
|
||||
if (o == null) fail("Response has no top-level facet named '" + expectedFacetName + "'");
|
||||
if (!(o instanceof NamedList)) fail("Response has a property for the expected facet '" + expectedFacetName + "' property, but it is not a NamedList");
|
||||
|
||||
final NamedList<Object> expectedFacetTopLevel = (NamedList<Object>) o;
|
||||
o = expectedFacetTopLevel.get("buckets");
|
||||
if (o == null) fail("Response has no 'buckets' property under 'facets'");
|
||||
if (!(o instanceof List)) fail("Response has no 'buckets' property containing actual facet information.");
|
||||
|
||||
final List<NamedList> bucketList = (List<NamedList>) o;
|
||||
assertEquals("Expected " + expectedBuckets.length + " buckets, but found " + bucketList.size(),
|
||||
expectedBuckets.length, bucketList.size());
|
||||
for (int i = 0; i < expectedBuckets.length; i++) {
|
||||
final FacetBucket expectedBucket = expectedBuckets[i];
|
||||
final NamedList<Object> actualBucket = bucketList.get(i);
|
||||
assertEquals(expectedBucket.getVal(), actualBucket.get("val"));
|
||||
assertEquals(expectedBucket.getCount(), actualBucket.get("count"));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFacetResponseHasStatFacetWithValue(NamedList<Object> facetResponse, String expectedFacetName, Double expectedStatValue) {
|
||||
Object o = facetResponse.get(expectedFacetName);
|
||||
if (o == null) fail("Response has no top-level facet named '" + expectedFacetName + "'");
|
||||
if (!(o instanceof Number)) fail("Response has a property for the expected facet '" + expectedFacetName + "' property, but it is not a Number");
|
||||
|
||||
final Number actualStatValueAsNumber = (Number) o;
|
||||
final Double actualStatValueAsDouble = ((Number) o).doubleValue();
|
||||
assertEquals(expectedStatValue, actualStatValueAsDouble, 0.5);
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@ import java.util.Map;
|
|||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.solr.client.solrj.request.RequestWriter;
|
||||
import org.apache.solr.client.solrj.request.json.JsonQueryRequest;
|
||||
import org.apache.solr.client.solrj.util.ClientUtils;
|
||||
import org.apache.solr.common.MapWriter;
|
||||
import org.junit.Test;
|
||||
|
@ -97,6 +96,91 @@ public class JsonQueryRequestUnitTest extends LuceneTestCase {
|
|||
assertThat(requestBody, containsString("\"query\":{\"lucene\":{\"q\":\"*:*\",\"df\":\"text\"}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidFacetName() {
|
||||
Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new JsonQueryRequest().withFacet(null, new HashMap<>());
|
||||
});
|
||||
assertThat(thrown.getMessage(),containsString("must be non-null"));
|
||||
|
||||
thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new JsonQueryRequest().withStatFacet(null, "avg(price)");
|
||||
});
|
||||
assertThat(thrown.getMessage(),containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidFacetMap() {
|
||||
Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new JsonQueryRequest().withFacet("anyFacetName", (Map<String, Object>)null);
|
||||
});
|
||||
assertThat(thrown.getMessage(),containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsNullFacetMapWriter() {
|
||||
Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new JsonQueryRequest().withFacet("anyFacetName", (MapWriter)null);
|
||||
});
|
||||
assertThat(thrown.getMessage(),containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidStatFacetString() {
|
||||
Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new JsonQueryRequest().withStatFacet("anyFacetName", (String)null);
|
||||
});
|
||||
assertThat(thrown.getMessage(),containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWritesProvidedFacetMapToJsonCorrectly() {
|
||||
final Map<String, Object> categoryFacetMap = new HashMap<>();
|
||||
categoryFacetMap.put("type", "terms");
|
||||
categoryFacetMap.put("field", "category");
|
||||
final JsonQueryRequest request = new JsonQueryRequest().withFacet("top_categories", categoryFacetMap);
|
||||
final String requestBody = writeRequestToJson(request);
|
||||
assertThat(requestBody, containsString("\"facet\":{\"top_categories\":{\"field\":\"category\",\"type\":\"terms\"}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWritesProvidedFacetMapWriterToJsonCorrectly() {
|
||||
final MapWriter facetWriter = new MapWriter() {
|
||||
@Override
|
||||
public void writeMap(EntryWriter ew) throws IOException {
|
||||
ew.put("type", "terms");
|
||||
ew.put("field", "category");
|
||||
}
|
||||
};
|
||||
final JsonQueryRequest request = new JsonQueryRequest().withFacet("top_categories", facetWriter);
|
||||
final String requestBody = writeRequestToJson(request);
|
||||
assertThat(requestBody, containsString("\"facet\":{\"top_categories\":{\"type\":\"terms\",\"field\":\"category\"}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWritesProvidedStatFacetToJsonCorrectly() {
|
||||
final JsonQueryRequest request = new JsonQueryRequest().withStatFacet("avg_price", "avg(price)");
|
||||
final String requestBody = writeRequestToJson(request);
|
||||
assertThat(requestBody, containsString("\"facet\":{\"avg_price\":\"avg(price)\"}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWritesMultipleFacetMapsToJsonCorrectly() {
|
||||
final Map<String, Object> facetMap1 = new HashMap<>();
|
||||
facetMap1.put("type", "terms");
|
||||
facetMap1.put("field", "a");
|
||||
final Map<String, Object> facetMap2 = new HashMap<>();
|
||||
facetMap2.put("type", "terms");
|
||||
facetMap2.put("field", "b");
|
||||
final JsonQueryRequest request = new JsonQueryRequest();
|
||||
|
||||
request.withFacet("facet1", facetMap1);
|
||||
request.withFacet("facet2", facetMap2);
|
||||
final String requestBody = writeRequestToJson(request);
|
||||
|
||||
assertThat(requestBody, containsString("\"facet\":{\"facet2\":{\"field\":\"b\",\"type\":\"terms\"},\"facet1\":{\"field\":\"a\",\"type\":\"terms\"}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidLimit() {
|
||||
Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.internal.matchers.StringContains.containsString;
|
||||
|
||||
public class QueryFacetMapTest extends SolrTestCaseJ4 {
|
||||
@Test
|
||||
public void testSetsFacetTypeToQuery() {
|
||||
final QueryFacetMap queryFacet = new QueryFacetMap("any:query");
|
||||
assertEquals("query", queryFacet.get("type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidQueryString() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
final QueryFacetMap queryFacet = new QueryFacetMap(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetsQueryWithCorrectKey() {
|
||||
final QueryFacetMap queryFacet = new QueryFacetMap("any:query");
|
||||
assertEquals("any:query", queryFacet.get("q"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.internal.matchers.StringContains.containsString;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RangeFacetMap}
|
||||
*/
|
||||
public class RangeFacetMapTest extends SolrTestCaseJ4 {
|
||||
@Test
|
||||
public void testRejectsInvalidFieldName() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new RangeFacetMap(null, 1, 2, 3);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidStartEndBounds() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new RangeFacetMap("ANY_FIELD_NAME", 1, -1, 3);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("'end' must be greater than parameter 'start'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidGap() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new RangeFacetMap("ANY_FIELD_NAME", 1, 2, -1);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be a positive integer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresRequiredValuesWithCorrectKeys() {
|
||||
final RangeFacetMap rangeFacet = new RangeFacetMap("ANY_FIELD_NAME", 1, 2, 3);
|
||||
assertEquals("ANY_FIELD_NAME", rangeFacet.get("field"));
|
||||
assertEquals(1L, rangeFacet.get("start"));
|
||||
assertEquals(2L, rangeFacet.get("end"));
|
||||
assertEquals(3L, rangeFacet.get("gap"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresHardEndWithCorrectKey() {
|
||||
final RangeFacetMap rangeFacet = new RangeFacetMap("ANY_FIELD_NAME", 1, 2, 3)
|
||||
.setHardEnd(true);
|
||||
assertEquals(true, rangeFacet.get("hardend"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidOtherBuckets() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new RangeFacetMap("ANY_FIELD_NAME", 1, 2, 3)
|
||||
.setOtherBuckets(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresOtherBucketsValueWithCorrectKey() {
|
||||
final RangeFacetMap rangeFacet = new RangeFacetMap("ANY_FIELD_NAME", 1, 2, 3)
|
||||
.setOtherBuckets(RangeFacetMap.OtherBuckets.BETWEEN);
|
||||
assertEquals("between", rangeFacet.get("other"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* 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.request.json;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.junit.Test;
|
||||
import static org.junit.internal.matchers.StringContains.containsString;
|
||||
|
||||
|
||||
public class TermsFacetMapTest extends SolrTestCaseJ4 {
|
||||
private static final String ANY_FIELD_NAME = "ANY_FIELD_NAME";
|
||||
|
||||
@Test
|
||||
public void testSetsFacetTypeToTerm() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME);
|
||||
assertEquals("terms", termsFacet.get("type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresFieldWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME);
|
||||
assertEquals(ANY_FIELD_NAME, termsFacet.get("field"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsNegativeBucketOffset() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setBucketOffset(-1);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-negative"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresBucketOffsetWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setBucketOffset(2);
|
||||
assertEquals(2, termsFacet.get("offset"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsNegativeBucketLimit() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setLimit(-1);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-negative"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresBucketLimitWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setLimit(3);
|
||||
assertEquals(3, termsFacet.get("limit"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidSortString() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setSort(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresSortWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setSort("price asc");
|
||||
assertEquals("price asc", termsFacet.get("sort"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectInvalidOverRequestBuckets() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setOverRequest(-2);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be >= -1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresOverRequestBucketsWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setOverRequest(4);
|
||||
assertEquals(4, termsFacet.get("overrequest"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresRefinementFlagWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.useDistributedFacetRefining(true);
|
||||
assertEquals(true, termsFacet.get("refine"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectInvalidOverRefineBuckets() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setOverRefine(-2);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be >= -1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresOverRefineBucketsWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setOverRefine(5);
|
||||
assertEquals(5, termsFacet.get("overrefine"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectInvalidMinCount() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setMinCount(0);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be a positive integer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresMinCountWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setMinCount(6);
|
||||
assertEquals(6, termsFacet.get("mincount"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresNumBucketsFlagWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.includeTotalNumBuckets(true);
|
||||
assertEquals(true, termsFacet.get("numBuckets"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresAllBucketsFlagWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.includeAllBucketsUnionBucket(true);
|
||||
assertEquals(true, termsFacet.get("allBuckets"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectInvalidTermPrefix() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setTermPrefix(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresTermPrefixWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setTermPrefix("ANY_PREF");
|
||||
assertEquals("ANY_PREF", termsFacet.get("prefix"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectsInvalidMethod() {
|
||||
final Throwable thrown = expectThrows(IllegalArgumentException.class, () -> {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setFacetMethod(null);
|
||||
});
|
||||
assertThat(thrown.getMessage(), containsString("must be non-null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresMethodWithCorrectKey() {
|
||||
final TermsFacetMap termsFacet = new TermsFacetMap(ANY_FIELD_NAME)
|
||||
.setFacetMethod(TermsFacetMap.FacetMethod.STREAM);
|
||||
assertEquals("stream", termsFacet.get("method"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue