mirror of https://github.com/apache/lucene.git
SOLR-15069: [child parentFilter=...] is now optional (#2181)
This commit is contained in:
parent
7b9f875145
commit
3147625890
|
@ -236,6 +236,9 @@ Improvements
|
||||||
|
|
||||||
* SOLR-15062: /api/cluster/zk/ls should give the stat of the current node (noble)
|
* SOLR-15062: /api/cluster/zk/ls should give the stat of the current node (noble)
|
||||||
|
|
||||||
|
* SOLR-15069: [child]: the parentFilter parameter is now fully optional and perhaps obsolete.
|
||||||
|
(David Smiley)
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
---------------------
|
---------------------
|
||||||
* SOLR-14975: Optimize CoreContainer.getAllCoreNames, getLoadedCoreNames and getCoreDescriptors. (Bruno Roustant)
|
* SOLR-14975: Optimize CoreContainer.getAllCoreNames, getLoadedCoreNames and getCoreDescriptors. (Bruno Roustant)
|
||||||
|
|
|
@ -28,14 +28,22 @@ import java.util.Map;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import org.apache.lucene.index.DocValues;
|
import org.apache.lucene.index.DocValues;
|
||||||
|
import org.apache.lucene.index.LeafReader;
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
|
import org.apache.lucene.index.PostingsEnum;
|
||||||
import org.apache.lucene.index.ReaderUtil;
|
import org.apache.lucene.index.ReaderUtil;
|
||||||
import org.apache.lucene.index.SortedDocValues;
|
import org.apache.lucene.index.SortedDocValues;
|
||||||
|
import org.apache.lucene.index.Terms;
|
||||||
|
import org.apache.lucene.index.TermsEnum;
|
||||||
|
import org.apache.lucene.search.DocIdSetIterator;
|
||||||
import org.apache.lucene.search.join.BitSetProducer;
|
import org.apache.lucene.search.join.BitSetProducer;
|
||||||
import org.apache.lucene.util.Bits;
|
|
||||||
import org.apache.lucene.util.BitSet;
|
import org.apache.lucene.util.BitSet;
|
||||||
|
import org.apache.lucene.util.Bits;
|
||||||
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.apache.solr.common.SolrDocument;
|
import org.apache.solr.common.SolrDocument;
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
|
import org.apache.solr.schema.IndexSchema;
|
||||||
|
import org.apache.solr.search.BitsFilteredPostingsEnum;
|
||||||
import org.apache.solr.search.DocSet;
|
import org.apache.solr.search.DocSet;
|
||||||
import org.apache.solr.search.SolrIndexSearcher;
|
import org.apache.solr.search.SolrIndexSearcher;
|
||||||
import org.apache.solr.search.SolrReturnFields;
|
import org.apache.solr.search.SolrReturnFields;
|
||||||
|
@ -52,20 +60,23 @@ class ChildDocTransformer extends DocTransformer {
|
||||||
private static final String ANON_CHILD_KEY = "_childDocuments_";
|
private static final String ANON_CHILD_KEY = "_childDocuments_";
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final BitSetProducer parentsFilter;
|
private final BitSetProducer parentsFilter; // if null; resolve parent via uniqueKey instead
|
||||||
private final DocSet childDocSet;
|
private final DocSet childDocSet;
|
||||||
private final int limit;
|
private final int limit;
|
||||||
private final boolean isNestedSchema;
|
private final boolean isNestedSchema;
|
||||||
private final SolrReturnFields childReturnFields;
|
private final SolrReturnFields childReturnFields;
|
||||||
|
private String[] extraRequestedFields;
|
||||||
|
|
||||||
ChildDocTransformer(String name, BitSetProducer parentsFilter, DocSet childDocSet,
|
ChildDocTransformer(String name, BitSetProducer parentsFilter, DocSet childDocSet,
|
||||||
SolrReturnFields returnFields, boolean isNestedSchema, int limit) {
|
SolrReturnFields returnFields, boolean isNestedSchema, int limit,
|
||||||
|
String uniqueKeyField) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.parentsFilter = parentsFilter;
|
this.parentsFilter = parentsFilter;
|
||||||
this.childDocSet = childDocSet;
|
this.childDocSet = childDocSet;
|
||||||
this.limit = limit;
|
this.limit = limit;
|
||||||
this.isNestedSchema = isNestedSchema;
|
this.isNestedSchema = isNestedSchema;
|
||||||
this.childReturnFields = returnFields!=null? returnFields: new SolrReturnFields();
|
this.childReturnFields = returnFields!=null? returnFields: new SolrReturnFields();
|
||||||
|
this.extraRequestedFields = parentsFilter == null ? new String[]{uniqueKeyField} : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -76,6 +87,37 @@ class ChildDocTransformer extends DocTransformer {
|
||||||
@Override
|
@Override
|
||||||
public boolean needsSolrIndexSearcher() { return true; }
|
public boolean needsSolrIndexSearcher() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getExtraRequestFields() {
|
||||||
|
return extraRequestedFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getPrevRootGivenFilter(LeafReaderContext leafReaderContext, int segRootId) throws IOException {
|
||||||
|
final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext);
|
||||||
|
if (segParentsBitSet != null) {
|
||||||
|
return segRootId == 0 ? -1 : segParentsBitSet.prevSetBit(segRootId - 1);
|
||||||
|
}
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||||
|
"Parent filter '" + parentsFilter + "' doesn't match any parent documents");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getPrevRootGivenId(LeafReaderContext leafReaderContext, int segRootId,
|
||||||
|
BytesRef idBytes) throws IOException {
|
||||||
|
final LeafReader reader = leafReaderContext.reader();
|
||||||
|
final Terms terms = reader.terms(IndexSchema.ROOT_FIELD_NAME); // never returns null here
|
||||||
|
final TermsEnum iterator = terms.iterator();
|
||||||
|
if (iterator.seekExact(idBytes)) {
|
||||||
|
PostingsEnum docs = iterator.postings(null, PostingsEnum.NONE);
|
||||||
|
docs = BitsFilteredPostingsEnum.wrap(docs, reader.getLiveDocs());
|
||||||
|
int id = docs.nextDoc();
|
||||||
|
if (id != DocIdSetIterator.NO_MORE_DOCS) {
|
||||||
|
assert id <= segRootId : "integrity violation";
|
||||||
|
return id - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return segRootId - 1; // thus no child docs
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transform(SolrDocument rootDoc, int rootDocId) {
|
public void transform(SolrDocument rootDoc, int rootDocId) {
|
||||||
// note: this algorithm works if both if we have have _nest_path_ and also if we don't!
|
// note: this algorithm works if both if we have have _nest_path_ and also if we don't!
|
||||||
|
@ -87,18 +129,25 @@ class ChildDocTransformer extends DocTransformer {
|
||||||
final List<LeafReaderContext> leaves = searcher.getIndexReader().leaves();
|
final List<LeafReaderContext> leaves = searcher.getIndexReader().leaves();
|
||||||
final int seg = ReaderUtil.subIndex(rootDocId, leaves);
|
final int seg = ReaderUtil.subIndex(rootDocId, leaves);
|
||||||
final LeafReaderContext leafReaderContext = leaves.get(seg);
|
final LeafReaderContext leafReaderContext = leaves.get(seg);
|
||||||
|
final Bits liveDocs = leafReaderContext.reader().getLiveDocs();
|
||||||
final int segBaseId = leafReaderContext.docBase;
|
final int segBaseId = leafReaderContext.docBase;
|
||||||
final int segRootId = rootDocId - segBaseId;
|
final int segRootId = rootDocId - segBaseId;
|
||||||
final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext);
|
|
||||||
final Bits liveDocs = leafReaderContext.reader().getLiveDocs();
|
|
||||||
|
|
||||||
if (segParentsBitSet == null) {
|
// can return be -1 and that's okay (happens for very first block)
|
||||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
final int segPrevRootId;
|
||||||
"Parent filter '" + parentsFilter + "' doesn't match any parent documents");
|
if (parentsFilter != null) {
|
||||||
|
segPrevRootId = getPrevRootGivenFilter(leafReaderContext, segRootId);
|
||||||
|
} else {
|
||||||
|
final IndexSchema schema = searcher.getSchema();
|
||||||
|
final String idStr = schema.printableUniqueKey(rootDoc);
|
||||||
|
if (idStr == null) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||||
|
"[child] requires fl to include the ID");
|
||||||
|
}
|
||||||
|
final BytesRef idBytes = schema.indexableUniqueKey(idStr);
|
||||||
|
segPrevRootId = getPrevRootGivenId(leafReaderContext, rootDocId, idBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
final int segPrevRootId = segRootId==0? -1: segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay
|
|
||||||
|
|
||||||
if (segPrevRootId == (segRootId - 1)) {
|
if (segPrevRootId == (segRootId - 1)) {
|
||||||
// doc has no children, return fast
|
// doc has no children, return fast
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -41,7 +41,9 @@ import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME;
|
||||||
/**
|
/**
|
||||||
* Attaches all descendants (child documents) to each parent document.
|
* Attaches all descendants (child documents) to each parent document.
|
||||||
*
|
*
|
||||||
* The "parentFilter" parameter is mandatory if the schema is not of nest/hierarchy.
|
* Optionally you can provide a "parentFilter" param to designate which documents are the root
|
||||||
|
* documents (parent-most documents). Solr can figure this out on its own but you might want to
|
||||||
|
* specify it.
|
||||||
*
|
*
|
||||||
* Optionally you can provide a "childFilter" param to filter out which child documents should be returned and a
|
* Optionally you can provide a "childFilter" param to filter out which child documents should be returned and a
|
||||||
* "limit" param which provides an option to specify the number of child documents
|
* "limit" param which provides an option to specify the number of child documents
|
||||||
|
@ -96,10 +98,7 @@ public class ChildDocTransformerFactory extends TransformerFactory {
|
||||||
// DocSet parentDocSet = req.getSearcher().getDocSet(parentFilterQuery);
|
// DocSet parentDocSet = req.getSearcher().getDocSet(parentFilterQuery);
|
||||||
// then return BitSetProducer with custom BitSet impl accessing the docSet
|
// then return BitSetProducer with custom BitSet impl accessing the docSet
|
||||||
if (parentFilterStr == null) {
|
if (parentFilterStr == null) {
|
||||||
if (!buildHierarchy) {
|
parentsFilter = !buildHierarchy ? null : new QueryBitSetProducer(rootFilter);
|
||||||
throw new SolrException(ErrorCode.BAD_REQUEST, "Parent filter should be sent as parentFilter=filterCondition");
|
|
||||||
}
|
|
||||||
parentsFilter = new QueryBitSetProducer(rootFilter);
|
|
||||||
} else {
|
} else {
|
||||||
if(buildHierarchy) {
|
if(buildHierarchy) {
|
||||||
throw new SolrException(ErrorCode.BAD_REQUEST, "Parent filter should not be sent when the schema is nested");
|
throw new SolrException(ErrorCode.BAD_REQUEST, "Parent filter should not be sent when the schema is nested");
|
||||||
|
@ -137,7 +136,8 @@ public class ChildDocTransformerFactory extends TransformerFactory {
|
||||||
|
|
||||||
int limit = params.getInt( "limit", 10 );
|
int limit = params.getInt( "limit", 10 );
|
||||||
|
|
||||||
return new ChildDocTransformer(field, parentsFilter, childDocSet, childSolrReturnFields, buildHierarchy, limit);
|
return new ChildDocTransformer(field, parentsFilter, childDocSet, childSolrReturnFields,
|
||||||
|
buildHierarchy, limit, req.getSchema().getUniqueKeyField().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Query parseQuery(String qstr, SolrQueryRequest req, String param) {
|
private static Query parseQuery(String qstr, SolrQueryRequest req, String param) {
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.apache.lucene.analysis.DelegatingAnalyzerWrapper;
|
||||||
import org.apache.lucene.index.IndexableField;
|
import org.apache.lucene.index.IndexableField;
|
||||||
import org.apache.lucene.queries.payloads.PayloadDecoder;
|
import org.apache.lucene.queries.payloads.PayloadDecoder;
|
||||||
import org.apache.lucene.search.similarities.Similarity;
|
import org.apache.lucene.search.similarities.Similarity;
|
||||||
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.apache.lucene.util.Version;
|
import org.apache.lucene.util.Version;
|
||||||
import org.apache.solr.common.ConfigNode;
|
import org.apache.solr.common.ConfigNode;
|
||||||
import org.apache.solr.common.MapSerializable;
|
import org.apache.solr.common.MapSerializable;
|
||||||
|
@ -359,6 +360,11 @@ public class IndexSchema {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Given a readable/printable uniqueKey value, return an indexable version */
|
||||||
|
public BytesRef indexableUniqueKey(String idStr) {
|
||||||
|
return new BytesRef(uniqueKeyFieldType.toInternal(idStr));
|
||||||
|
}
|
||||||
|
|
||||||
private SchemaField getIndexedField(String fname) {
|
private SchemaField getIndexedField(String fname) {
|
||||||
SchemaField f = getFields().get(fname);
|
SchemaField f = getFields().get(fname);
|
||||||
if (f==null) {
|
if (f==null) {
|
||||||
|
|
|
@ -96,13 +96,14 @@ public class TestChildDocTransformer extends SolrTestCaseJ4 {
|
||||||
"/response/result/doc[1]/doc[2]/str[@name='id']='5'"};
|
"/response/result/doc[1]/doc[2]/str[@name='id']='5'"};
|
||||||
|
|
||||||
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1);
|
"fl", "*,[child]"), test1);
|
||||||
|
|
||||||
|
// shows parentFilter specified (not necessary any more) and also child
|
||||||
|
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
|
"fl", "id, subject,[child childFilter=\"title:foo\"]"), test2);
|
||||||
|
|
||||||
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "id, subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2);
|
"fl", "id, subject,[child childFilter=\"title:bar\" limit=2]"), test3);
|
||||||
|
|
||||||
assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
|
||||||
"fl", "id, subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:bar\" limit=2]"), test3);
|
|
||||||
|
|
||||||
SolrException e = expectThrows(SolrException.class, () -> {
|
SolrException e = expectThrows(SolrException.class, () -> {
|
||||||
h.query(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
h.query(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
|
@ -240,13 +241,13 @@ public class TestChildDocTransformer extends SolrTestCaseJ4 {
|
||||||
};
|
};
|
||||||
|
|
||||||
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1);
|
"fl", "*,[child]"), test1);
|
||||||
|
|
||||||
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "id, subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2);
|
"fl", "id, subject,[child childFilter=\"title:foo\"]"), test2);
|
||||||
|
|
||||||
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "id, subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:bar\" limit=3]"), test3);
|
"fl", "id, subject,[child childFilter=\"title:bar\" limit=3]"), test3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testChildDocNonStoredDVFields() throws Exception {
|
private void testChildDocNonStoredDVFields() throws Exception {
|
||||||
|
@ -271,26 +272,26 @@ public class TestChildDocTransformer extends SolrTestCaseJ4 {
|
||||||
};
|
};
|
||||||
|
|
||||||
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1);
|
"fl", "*,[child]"), test1);
|
||||||
|
|
||||||
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "intDvoDefault, subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2);
|
"fl", "intDvoDefault, subject,[child childFilter=\"title:foo\"]"), test2);
|
||||||
|
|
||||||
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "intDvoDefault, subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:bar\" limit=2]"), test3);
|
"fl", "intDvoDefault, subject,[child childFilter=\"title:bar\" limit=2]"), test3);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testChildReturnFields() throws Exception {
|
private void testChildReturnFields() throws Exception {
|
||||||
|
|
||||||
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "*,[child parentFilter=\"subject:parentDocument\" fl=\"intDvoDefault,child_fl:[value v='child_fl_test']\"]"),
|
"fl", "*,[child fl=\"intDvoDefault,child_fl:[value v='child_fl_test']\"]"),
|
||||||
"/response/docs/[0]/intDefault==42",
|
"/response/docs/[0]/intDefault==42",
|
||||||
"/response/docs/[0]/_childDocuments_/[0]/intDvoDefault==42",
|
"/response/docs/[0]/_childDocuments_/[0]/intDvoDefault==42",
|
||||||
"/response/docs/[0]/_childDocuments_/[0]/child_fl=='child_fl_test'");
|
"/response/docs/[0]/_childDocuments_/[0]/child_fl=='child_fl_test'");
|
||||||
|
|
||||||
try(SolrQueryRequest req = req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
try(SolrQueryRequest req = req("q", "*:*", "fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "intDefault,[child parentFilter=\"subject:parentDocument\" fl=\"intDvoDefault, [docid]\"]")) {
|
"fl", "intDefault,[child fl=\"intDvoDefault, [docid]\"]")) {
|
||||||
BasicResultContext res = (BasicResultContext) h.queryAndResponse("/select", req).getResponse();
|
BasicResultContext res = (BasicResultContext) h.queryAndResponse("/select", req).getResponse();
|
||||||
Iterator<SolrDocument> docsStreamer = res.getProcessedDocuments();
|
Iterator<SolrDocument> docsStreamer = res.getProcessedDocuments();
|
||||||
while (docsStreamer.hasNext()) {
|
while (docsStreamer.hasNext()) {
|
||||||
|
@ -401,6 +402,14 @@ public class TestChildDocTransformer extends SolrTestCaseJ4 {
|
||||||
"fl", "id, cat, title, [child childFilter='cat:childDocument' parentFilter=\"subject:parentDocument\"]"),
|
"fl", "id, cat, title, [child childFilter='cat:childDocument' parentFilter=\"subject:parentDocument\"]"),
|
||||||
tests);
|
tests);
|
||||||
|
|
||||||
|
// shows if parentFilter matches all docs, then there are effectively no child docs
|
||||||
|
assertJQ(req("q", "*:*",
|
||||||
|
"sort", "id asc",
|
||||||
|
"fq", "subject:\"parentDocument\" ",
|
||||||
|
"fl", "id,[child childFilter='cat:childDocument' parentFilter=\"*:*\"]"),
|
||||||
|
"/response==" +
|
||||||
|
"{'numFound':2,'start':0,'numFoundExact':true,'docs':[{'id':'1'},{'id':'4'}]}");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testSubQueryParentFilterJSON() throws Exception {
|
private void testSubQueryParentFilterJSON() throws Exception {
|
||||||
|
@ -451,13 +460,13 @@ public class TestChildDocTransformer extends SolrTestCaseJ4 {
|
||||||
assertQ(req("q", "*:*",
|
assertQ(req("q", "*:*",
|
||||||
"sort", "id asc",
|
"sort", "id asc",
|
||||||
"fq", "subject:\"parentDocument\" ",
|
"fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "*,[child childFilter='cat:childDocument' parentFilter=\"subject:parentDocument\"]"),
|
"fl", "*,[child childFilter='cat:childDocument']"),
|
||||||
tests);
|
tests);
|
||||||
|
|
||||||
assertQ(req("q", "*:*",
|
assertQ(req("q", "*:*",
|
||||||
"sort", "id asc",
|
"sort", "id asc",
|
||||||
"fq", "subject:\"parentDocument\" ",
|
"fq", "subject:\"parentDocument\" ",
|
||||||
"fl", "id, cat, title, [child childFilter='cat:childDocument' parentFilter=\"subject:parentDocument\"]"),
|
"fl", "id, cat, title, [child childFilter='cat:childDocument']"),
|
||||||
tests);
|
tests);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,14 +132,11 @@ Note that this transformer can be used even when the query used to match the res
|
||||||
|
|
||||||
[source,plain]
|
[source,plain]
|
||||||
----
|
----
|
||||||
q=book_title:Solr&fl=id,[child parentFilter=doc_type:book childFilter=doc_type:chapter limit=100]
|
q=book_title:Solr&fl=id,[child childFilter=doc_type:chapter limit=100]
|
||||||
----
|
----
|
||||||
|
|
||||||
If the documents involved include a `\_nest_path_` field, then it is used to re-create the hierarchical structure of the descendent documents using the original psuedo-field names the documents were indexed with, otherwise the descendent documents are returned as a flat list of <<indexing-nested-documents#indexing-anonymous-children,anonymous children>>.
|
If the documents involved include a `\_nest_path_` field, then it is used to re-create the hierarchical structure of the descendent documents using the original psuedo-field names the documents were indexed with, otherwise the descendent documents are returned as a flat list of <<indexing-nested-documents#indexing-anonymous-children,anonymous children>>.
|
||||||
|
|
||||||
`parentFilter`::
|
|
||||||
When using a schema that does _not_ include the `\_nest_path_` field, this parameter is mandatory, and serves the same purpose as the `of`/`which` parms in `{!child}`/`{!parent}` query parsers: to identify the set of "all parents" for the purpose of identifying the begining & end of each nested document block. *When a schema _does_ include a `\_nest_path_` field, this parameter is prohibited.*
|
|
||||||
|
|
||||||
`childFilter`::
|
`childFilter`::
|
||||||
A query to filter which child documents should be included. This can be particularly useful when you have multiple levels of hierarchical documents. The default is all children.
|
A query to filter which child documents should be included. This can be particularly useful when you have multiple levels of hierarchical documents. The default is all children.
|
||||||
|
|
||||||
|
@ -151,6 +148,12 @@ The field list which the transformer is to return. The default is the top level
|
||||||
+
|
+
|
||||||
There is a further limitation in which the fields here should be a subset of those specified by the top level `fl` parameter.
|
There is a further limitation in which the fields here should be a subset of those specified by the top level `fl` parameter.
|
||||||
|
|
||||||
|
`parentFilter`::
|
||||||
|
Serves the same purpose as the `of`/`which` params in `{!child}`/`{!parent}` query parsers: to
|
||||||
|
identify the set of "all parents" for the purpose of identifying the beginning & end of each
|
||||||
|
nested document block. This recently became fully optional and appears to be obsolete.
|
||||||
|
It is likely to be removed in a future Solr release, so _if you find it has some use, let the
|
||||||
|
project know!_
|
||||||
|
|
||||||
[TIP]
|
[TIP]
|
||||||
====
|
====
|
||||||
|
|
Loading…
Reference in New Issue