[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla closed the pull request at: https://github.com/apache/lucene-solr/pull/416 --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213543737 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -124,10 +124,11 @@ public void testParentFilterLimitJSON() throws Exception { assertJQ(req("q", "type_s:donut", "sort", "id asc", -"fl", "id, type_s, toppings, _nest_path_, [child limit=1]", +"fl", "id, type_s, lonely, lonelyGrandChild, test_s, test2_s, _nest_path_, [child limit=1]", --- End diff -- Leaving this as a TODO for another day sounds like a decent option. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213541579 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -124,10 +124,11 @@ public void testParentFilterLimitJSON() throws Exception { assertJQ(req("q", "type_s:donut", "sort", "id asc", -"fl", "id, type_s, toppings, _nest_path_, [child limit=1]", +"fl", "id, type_s, lonely, lonelyGrandChild, test_s, test2_s, _nest_path_, [child limit=1]", --- End diff -- To my point I wrote in JIRA: It's sad that when I see this I have no idea if it's right/wrong without having to go look at indexSampleData then think about it. No? (this isn't a critique of you in particular; lots of tests including some I've written look like the current tests here). imagine one doc with some nested docs, all of which only have their ID. Since they only have their ID, it's not a lot of literal text in JSON. The BeforeClass unmatched docs cold use negative IDs to easily know who's who. Any way if you would rather leave this as a "TODO" for another day then I understand. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213540255 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -123,6 +124,16 @@ public void transform(SolrDocument rootDoc, int rootDocId) { // Do we need to do anything with this doc (either ancestor or matched the child query) if (isAncestor || childDocSet == null || childDocSet.exists(docId)) { + + if(limit != -1) { +if(!isAncestor) { + if(matches == limit) { +continue; + } + ++matches; --- End diff -- I think matches should be incremented if it's in childDocSet (includes childDocSet being null). Wether it's an ancestor or not doesn't matter I think. You could pull out a new variable isInChildDocSet. Or I suppose simply consider all a match; what I see what you just did as I write this; that's fine too. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213321225 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -109,9 +109,14 @@ public void transform(SolrDocument rootDoc, int rootDocId) { // Loop each child ID up to the parent (exclusive). for (int docId = calcDocIdToIterateFrom(lastChildId, rootDocId); docId < rootDocId; ++docId) { -// get the path. (note will default to ANON_CHILD_KEY if not in schema or oddly blank) +// get the path. (note will default to ANON_CHILD_KEY if schema is not nested or empty string if blank) String fullDocPath = getPathByDocId(docId - segBaseId, segPathDocValues); +if(isNestedSchema && !fullDocPath.contains(transformedDocPath)) { + // is not a descendant of the transformed doc, return fast. + return; --- End diff -- Added another query to [TestChildDocumentHierarchy#testNonRootChildren](https://github.com/apache/lucene-solr/pull/416/files#diff-9fe0ab006f82be5c6a07d5bb6dbc6da0R243). This test failed before I changed the return to continue(previous commit), and passes using the latest. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213319927 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -99,6 +96,9 @@ public void transform(SolrDocument rootDoc, int rootDocId) { // we'll need this soon... final SortedDocValues segPathDocValues = DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME); + // passing a different SortedDocValues obj since the child documents which come after are of smaller docIDs, + // and the iterator can not be reversed. + final String transformedDocPath = getPathByDocId(segRootId, DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME)); --- End diff -- Sure thing. Done :-) --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213306038 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -109,9 +109,14 @@ public void transform(SolrDocument rootDoc, int rootDocId) { // Loop each child ID up to the parent (exclusive). for (int docId = calcDocIdToIterateFrom(lastChildId, rootDocId); docId < rootDocId; ++docId) { -// get the path. (note will default to ANON_CHILD_KEY if not in schema or oddly blank) +// get the path. (note will default to ANON_CHILD_KEY if schema is not nested or empty string if blank) String fullDocPath = getPathByDocId(docId - segBaseId, segPathDocValues); +if(isNestedSchema && !fullDocPath.contains(transformedDocPath)) { + // is not a descendant of the transformed doc, return fast. + return; --- End diff -- Yep, you're right. I'll investigate further to see why a test did not fail because of this. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213295374 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -99,6 +96,9 @@ public void transform(SolrDocument rootDoc, int rootDocId) { // we'll need this soon... final SortedDocValues segPathDocValues = DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME); + // passing a different SortedDocValues obj since the child documents which come after are of smaller docIDs, + // and the iterator can not be reversed. + final String transformedDocPath = getPathByDocId(segRootId, DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME)); --- End diff -- Can you call this rootDocPath since we refer to this doc as the "root doc" elsewhere here? I can see why you chose this name. You could add a comment that the "root doc" is the input doc we are adding information to, and is usually but not necessarily the root of the block of documents (i.e. the root doc may itself be a child doc of another doc). --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213291939 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -0,0 +1,263 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.ReaderUtil; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.util.BitSet; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class ChildDocTransformer extends DocTransformer { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final String ANON_CHILD_KEY = "_childDocuments_"; + + private final String name; + private final BitSetProducer parentsFilter; + private final DocSet childDocSet; + private final int limit; + private final boolean isNestedSchema; + + private final SolrReturnFields childReturnFields = new SolrReturnFields(); + + ChildDocTransformer(String name, BitSetProducer parentsFilter, + DocSet childDocSet, boolean isNestedSchema, int limit) { +this.name = name; +this.parentsFilter = parentsFilter; +this.childDocSet = childDocSet; +this.limit = limit; +this.isNestedSchema = isNestedSchema; + } + + @Override + public String getName() { +return name; + } + + @Override + 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! + +try { + + // lookup what the *previous* rootDocId is, and figure which segment this is + final SolrIndexSearcher searcher = context.getSearcher(); + final List leaves = searcher.getIndexReader().leaves(); + final int seg = ReaderUtil.subIndex(rootDocId, leaves); + final LeafReaderContext leafReaderContext = leaves.get(seg); + final int segBaseId = leafReaderContext.docBase; + final int segRootId = rootDocId - segBaseId; + final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); + + final int segPrevRootId = segRootId==0? -1: segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + if(segPrevRootId == (segRootId - 1)) { +// doc has no children, return fast +return; + } + + // we'll need this soon... + final SortedDocValues segPathDocValues = DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME); + // passing a different SortedDocValues obj since the child documents which come after are of smaller docIDs, + // and the iterator can not be reversed. + final String transformedDocPath = getPathByDocId(segRootId, DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME)); + +
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213292329 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -109,9 +109,14 @@ public void transform(SolrDocument rootDoc, int rootDocId) { // Loop each child ID up to the parent (exclusive). for (int docId = calcDocIdToIterateFrom(lastChildId, rootDocId); docId < rootDocId; ++docId) { -// get the path. (note will default to ANON_CHILD_KEY if not in schema or oddly blank) +// get the path. (note will default to ANON_CHILD_KEY if schema is not nested or empty string if blank) String fullDocPath = getPathByDocId(docId - segBaseId, segPathDocValues); +if(isNestedSchema && !fullDocPath.contains(transformedDocPath)) { + // is not a descendant of the transformed doc, return fast. + return; --- End diff -- woah; shouldn't this be "continue"? We should have a test that would fail on this bug. All it would take would be an additional child doc that is not underneath the input root/transformed doc but follows it (as provided on input). Some of the first docs we iterate over here might not descend from rootDocId --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213270075 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -0,0 +1,263 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.ReaderUtil; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.util.BitSet; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class ChildDocTransformer extends DocTransformer { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final String ANON_CHILD_KEY = "_childDocuments_"; + + private final String name; + private final BitSetProducer parentsFilter; + private final DocSet childDocSet; + private final int limit; + private final boolean isNestedSchema; + + private final SolrReturnFields childReturnFields = new SolrReturnFields(); + + ChildDocTransformer(String name, BitSetProducer parentsFilter, + DocSet childDocSet, boolean isNestedSchema, int limit) { +this.name = name; +this.parentsFilter = parentsFilter; +this.childDocSet = childDocSet; +this.limit = limit; +this.isNestedSchema = isNestedSchema; + } + + @Override + public String getName() { +return name; + } + + @Override + 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! + +try { + + // lookup what the *previous* rootDocId is, and figure which segment this is + final SolrIndexSearcher searcher = context.getSearcher(); + final List leaves = searcher.getIndexReader().leaves(); + final int seg = ReaderUtil.subIndex(rootDocId, leaves); + final LeafReaderContext leafReaderContext = leaves.get(seg); + final int segBaseId = leafReaderContext.docBase; + final int segRootId = rootDocId - segBaseId; + final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); + + final int segPrevRootId = segRootId==0? -1: segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + if(segPrevRootId == (segRootId - 1)) { +// doc has no children, return fast +return; + } + + // we'll need this soon... + final SortedDocValues segPathDocValues = DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME); + // passing a different SortedDocValues obj since the child documents which come after are of smaller docIDs, + // and the iterator can not be reversed. + final String transformedDocPath = getPathByDocId(segRootId, DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME)); + +
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213202931 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -238,6 +238,17 @@ public void testSingularChildFilterJSON() throws Exception { tests); } + @Test + public void testNonRootChildren() throws Exception { +indexSampleData(numberOfDocsPerNestedTest); +assertJQ(req("q", "test_s:testing", +"sort", "id asc", +"fl", "*,[child childFilter='lonely/lonelyGrandChild/test2_s:secondTest' parentFilter='_nest_path_:\"lonely/\"']", --- End diff -- Perhaps I could try and remove the parentFilter requirement all together for hierarchy based queries? --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r212993235 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -92,6 +97,18 @@ private void testChildDoctransformerXML() { assertQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ", "fl", "subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); +try(SolrQueryRequest req = req("q", "*:*", "fq", "subject:\"parentDocument\" ", --- End diff -- Please include a comment explaining what it is you're testing here (i.e. what is the point of this particular test); it's non-obvious to me. This test tests manually... but it could be done more concisely using the XPath style assertions done elsewhere in this file and most tests. For example if you want to test the number of child documents, it'd be something like this: "count(/response/result/doc[1]/doc)=2" and include a numFound check. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r213001520 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -238,6 +238,17 @@ public void testSingularChildFilterJSON() throws Exception { tests); } + @Test + public void testNonRootChildren() throws Exception { +indexSampleData(numberOfDocsPerNestedTest); +assertJQ(req("q", "test_s:testing", +"sort", "id asc", +"fl", "*,[child childFilter='lonely/lonelyGrandChild/test2_s:secondTest' parentFilter='_nest_path_:\"lonely/\"']", --- End diff -- Aha; I see you're requiring that a custom parentFilter be present in order to use the childDocFilter on non-root docs. I suppose that's fine; it something that could be enhanced in the future to avoid that inconvenience & limitation(*). Since this is now required, the transformer should throw an error to the user if the doc to be transformed isn't in that filter. For example: "The document to be transformed must be matched by the parentFilter of the child transformer" with BAD_REQUEST flag as it's user-error. And add a test for this. (*) An example of it being a limitation is this: Say the child docs are all a "comment" in nature; and thus are recursive. The top query "q" might match certain comments of interest. And we want all children of those comments returned hierarchically. The parentFilter would end up having to match all comments, but that would prevent returning child comments and thus blocking the whole idea altogether :-( To "fix" this limitation, we'd not constrain transformed docs to those in the parentFilter, and we wouldn't insist on any special parentFilter. In the loop that builds the hierarchy, when fetching the path, we'd need to skip over the current doc if it doesn't descend from that of the doc to be transformed. Seems pretty straight-forward. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211599253 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -0,0 +1,257 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.ReaderUtil; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.util.BitSet; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class ChildDocTransformer extends DocTransformer { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final String ANON_CHILD_KEY = "_childDocuments_"; + + private final String name; + private final BitSetProducer parentsFilter; + private final DocSet childDocSet; + private final int limit; + + private final SolrReturnFields childReturnFields = new SolrReturnFields(); + + ChildDocTransformer(String name, BitSetProducer parentsFilter, + DocSet childDocSet, int limit) { +this.name = name; +this.parentsFilter = parentsFilter; +this.childDocSet = childDocSet; +this.limit = limit; + } + + @Override + public String getName() { +return name; + } + + @Override + 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! + +try { + + // lookup what the *previous* rootDocId is, and figure which segment this is + final SolrIndexSearcher searcher = context.getSearcher(); + final List leaves = searcher.getIndexReader().leaves(); + final int seg = ReaderUtil.subIndex(rootDocId, leaves); + final LeafReaderContext leafReaderContext = leaves.get(seg); + final int segBaseId = leafReaderContext.docBase; + final int segRootId = rootDocId - segBaseId; + final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); + final int segPrevRootId = segRootId==0? -1: segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + if(segPrevRootId == (segRootId - 1)) { +// doc has no children, return fast +return; + } + + // we'll need this soon... + final SortedDocValues segPathDocValues = DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME); + + // the key in the Map is the document's ancestors key(one above the parent), while the key in the intermediate + // MultiMap is the direct child document's key(of the parent document) + Map> pendingParentPathsToChildren = new HashMap<>(); + + IndexSchema schema = searcher.getSc
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211595662 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -0,0 +1,257 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.ReaderUtil; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.util.BitSet; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class ChildDocTransformer extends DocTransformer { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final String ANON_CHILD_KEY = "_childDocuments_"; + + private final String name; + private final BitSetProducer parentsFilter; + private final DocSet childDocSet; + private final int limit; + + private final SolrReturnFields childReturnFields = new SolrReturnFields(); + + ChildDocTransformer(String name, BitSetProducer parentsFilter, + DocSet childDocSet, int limit) { +this.name = name; +this.parentsFilter = parentsFilter; +this.childDocSet = childDocSet; +this.limit = limit; + } + + @Override + public String getName() { +return name; + } + + @Override + 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! + +try { + + // lookup what the *previous* rootDocId is, and figure which segment this is + final SolrIndexSearcher searcher = context.getSearcher(); + final List leaves = searcher.getIndexReader().leaves(); + final int seg = ReaderUtil.subIndex(rootDocId, leaves); + final LeafReaderContext leafReaderContext = leaves.get(seg); + final int segBaseId = leafReaderContext.docBase; + final int segRootId = rootDocId - segBaseId; + final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); + final int segPrevRootId = segRootId==0? -1: segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + if(segPrevRootId == (segRootId - 1)) { +// doc has no children, return fast +return; + } + + // we'll need this soon... + final SortedDocValues segPathDocValues = DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME); + + // the key in the Map is the document's ancestors key(one above the parent), while the key in the intermediate + // MultiMap is the direct child document's key(of the parent document) + Map> pendingParentPathsToChildren = new HashMap<>(); + + IndexSchema schema = searcher.getSch
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211595043 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -0,0 +1,346 @@ +/* + * 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.response.transform; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import com.google.common.collect.Iterables; +import org.apache.lucene.index.IndexableField; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.BasicResultContext; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestChildDocTransformerHierarchy extends SolrTestCaseJ4 { + + private static AtomicInteger counter = new AtomicInteger(); + private static final String[] types = {"donut", "cake"}; + private static final String[] ingredients = {"flour", "cocoa", "vanilla"}; + private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); + private static final String[] names = {"Yaz", "Jazz", "Costa"}; + private static final String[] fieldsToRemove = {"_nest_parent_", "_nest_path_", "_root_"}; + private static final int sumOfDocsPerNestedDocument = 8; + private static final int numberOfDocsPerNestedTest = 10; + private static int randomDocTopId = 0; + private static String fqToExcludeNoneTestedDocs; // filter documents that were created for random segments to ensure the transformer works with multiple segments. + + @BeforeClass + public static void beforeClass() throws Exception { +initCore("solrconfig-update-processor-chains.xml", "schema-nest.xml"); // use "nest" schema +final boolean useSegments = random().nextBoolean(); +if(useSegments) { + // create random segments + final int numOfDocs = 10; + for(int i = 0; i < numOfDocs; ++i) { +updateJ(generateDocHierarchy(i), params("update.chain", "nested")); +if(random().nextBoolean()) { + assertU(commit()); +} + } + assertU(commit()); + randomDocTopId = counter.get(); --- End diff -- I think this line should be at the end of this method, executed always. I can see it works where it is now, executed conditionally, but I think it'd be clearer if not done in a condition. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211594271 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -0,0 +1,346 @@ +/* + * 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.response.transform; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import com.google.common.collect.Iterables; +import org.apache.lucene.index.IndexableField; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.BasicResultContext; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestChildDocTransformerHierarchy extends SolrTestCaseJ4 { + + private static AtomicInteger counter = new AtomicInteger(); + private static final String[] types = {"donut", "cake"}; + private static final String[] ingredients = {"flour", "cocoa", "vanilla"}; + private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); + private static final String[] names = {"Yaz", "Jazz", "Costa"}; + private static final String[] fieldsToRemove = {"_nest_parent_", "_nest_path_", "_root_"}; + private static final int sumOfDocsPerNestedDocument = 8; + private static final int numberOfDocsPerNestedTest = 10; + private static int randomDocTopId = 0; + private static String fqToExcludeNoneTestedDocs; // filter documents that were created for random segments to ensure the transformer works with multiple segments. --- End diff -- None -> Non --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211594079 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -0,0 +1,346 @@ +/* + * 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.response.transform; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import com.google.common.collect.Iterables; +import org.apache.lucene.index.IndexableField; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.BasicResultContext; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestChildDocTransformerHierarchy extends SolrTestCaseJ4 { + + private static AtomicInteger counter = new AtomicInteger(); + private static final String[] types = {"donut", "cake"}; + private static final String[] ingredients = {"flour", "cocoa", "vanilla"}; + private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); + private static final String[] names = {"Yaz", "Jazz", "Costa"}; + private static final String[] fieldsToRemove = {"_nest_parent_", "_nest_path_", "_root_"}; + private static final int sumOfDocsPerNestedDocument = 8; + private static final int numberOfDocsPerNestedTest = 10; + private static int randomDocTopId = 0; --- End diff -- I suggest rename to "firstTestedDocId". And rename "counter" to "idCounter". --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211519256 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -264,7 +309,7 @@ private static Object cleanIndexableField(Object field) { } private static String grandChildDocTemplate(int id) { -int docNum = id / 8; // the index of docs sent to solr in the AddUpdateCommand. e.g. first doc is 0 +int docNum = (id / sumOfDocsPerNestedDocument) % numberOfDocsPerNestedTest; // the index of docs sent to solr in the AddUpdateCommand. e.g. first doc is 0 --- End diff -- I tried simplifying the calculation a bit, [here's a link](https://github.com/apache/lucene-solr/pull/416/files#diff-9fe0ab006f82be5c6a07d5bb6dbc6da0R299). --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211505124 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -264,7 +309,7 @@ private static Object cleanIndexableField(Object field) { } private static String grandChildDocTemplate(int id) { -int docNum = id / 8; // the index of docs sent to solr in the AddUpdateCommand. e.g. first doc is 0 +int docNum = (id / sumOfDocsPerNestedDocument) % numberOfDocsPerNestedTest; // the index of docs sent to solr in the AddUpdateCommand. e.g. first doc is 0 --- End diff -- the modulo is added to filter the docs in the other segments, and then calculate the i that was passed, when constructing the nested document. This then ensures the child transformer did not fail. If we can be satisfied by only testing the ids, which does not seem as bullet-proof to me, this could be removed, and only the ids will be tested. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211495503 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -40,22 +43,52 @@ private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); private static final String[] names = {"Yaz", "Jazz", "Costa"}; private static final String[] fieldsToRemove = {"_nest_parent_", "_nest_path_", "_root_"}; + private static final int sumOfDocsPerNestedDocument = 8; + private static final int numberOfDocsPerNestedTest = 10; + private static boolean useSegments; + private static int randomDocTopId = 0; + private static String filterOtherSegments; @BeforeClass public static void beforeClass() throws Exception { initCore("solrconfig-update-processor-chains.xml", "schema-nest.xml"); // use "nest" schema +useSegments = random().nextBoolean(); +if(useSegments) { + final int numOfDocs = 10; + for(int i = 0; i < numOfDocs; ++i) { +updateJ(generateDocHierarchy(i), params("update.chain", "nested")); +if(random().nextBoolean()) { + assertU(commit()); +} + } + assertU(commit()); + randomDocTopId = counter.get(); + filterOtherSegments = "{!frange l=" + randomDocTopId + " incl=false}idInt"; +} else { + filterOtherSegments = "*:*"; +} } @After public void after() throws Exception { -clearIndex(); +if (!useSegments) { --- End diff -- So I could simply use delQ using the filter, great :-). --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211315461 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -40,22 +43,52 @@ private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); private static final String[] names = {"Yaz", "Jazz", "Costa"}; private static final String[] fieldsToRemove = {"_nest_parent_", "_nest_path_", "_root_"}; + private static final int sumOfDocsPerNestedDocument = 8; + private static final int numberOfDocsPerNestedTest = 10; + private static boolean useSegments; + private static int randomDocTopId = 0; + private static String filterOtherSegments; @BeforeClass public static void beforeClass() throws Exception { initCore("solrconfig-update-processor-chains.xml", "schema-nest.xml"); // use "nest" schema +useSegments = random().nextBoolean(); +if(useSegments) { + final int numOfDocs = 10; + for(int i = 0; i < numOfDocs; ++i) { +updateJ(generateDocHierarchy(i), params("update.chain", "nested")); +if(random().nextBoolean()) { + assertU(commit()); +} + } + assertU(commit()); + randomDocTopId = counter.get(); + filterOtherSegments = "{!frange l=" + randomDocTopId + " incl=false}idInt"; +} else { + filterOtherSegments = "*:*"; +} } @After public void after() throws Exception { -clearIndex(); +if (!useSegments) { --- End diff -- You could keep this if you want (use BeforeClass but have delete query in after()). But if you do, it could be simplified: Don't conditionally call "clearIndex"; simply always do this delete. And there's a simpler overloaded version -- `delQ(queryhere)` --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211258577 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -40,22 +43,52 @@ private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); private static final String[] names = {"Yaz", "Jazz", "Costa"}; private static final String[] fieldsToRemove = {"_nest_parent_", "_nest_path_", "_root_"}; + private static final int sumOfDocsPerNestedDocument = 8; --- End diff -- I understand we need a filter query here that can be referenced by the test, but I'm a bit dubious on all these other ones. You may very well have written the test in such a way that they are necessary at the moment, but lets consider how to simplify. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211255381 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -40,22 +43,52 @@ private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); private static final String[] names = {"Yaz", "Jazz", "Costa"}; private static final String[] fieldsToRemove = {"_nest_parent_", "_nest_path_", "_root_"}; + private static final int sumOfDocsPerNestedDocument = 8; + private static final int numberOfDocsPerNestedTest = 10; + private static boolean useSegments; + private static int randomDocTopId = 0; + private static String filterOtherSegments; @BeforeClass public static void beforeClass() throws Exception { initCore("solrconfig-update-processor-chains.xml", "schema-nest.xml"); // use "nest" schema +useSegments = random().nextBoolean(); +if(useSegments) { + final int numOfDocs = 10; + for(int i = 0; i < numOfDocs; ++i) { +updateJ(generateDocHierarchy(i), params("update.chain", "nested")); +if(random().nextBoolean()) { + assertU(commit()); +} + } + assertU(commit()); + randomDocTopId = counter.get(); + filterOtherSegments = "{!frange l=" + randomDocTopId + " incl=false}idInt"; +} else { + filterOtherSegments = "*:*"; +} } @After public void after() throws Exception { -clearIndex(); +if (!useSegments) { --- End diff -- oh my suggestion of beforeClass didn't consider that you might have to do this partial deletion. To make this simpler, move the new document addition stuff in beforeClass into a before() (and make pertinent fields non-static), then you can revert this after() to as it was. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211257783 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -40,22 +43,52 @@ private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); private static final String[] names = {"Yaz", "Jazz", "Costa"}; private static final String[] fieldsToRemove = {"_nest_parent_", "_nest_path_", "_root_"}; + private static final int sumOfDocsPerNestedDocument = 8; + private static final int numberOfDocsPerNestedTest = 10; + private static boolean useSegments; + private static int randomDocTopId = 0; + private static String filterOtherSegments; --- End diff -- minor point: rename `filterOtherSegments` to `fqToExcludeNontestedDocs` and add a comment explaining *why* we even have non-tested docs -- it's to perturb the Lucene segments a bit to ensure the transformer works with and without docs in other segments --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211259141 --- Diff: solr/core/src/test-files/solr/collection1/conf/schema-nest.xml --- @@ -20,6 +20,9 @@ + --- End diff -- minor: rename to `id_i` to follow typical naming conventions --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211256948 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -264,7 +309,7 @@ private static Object cleanIndexableField(Object field) { } private static String grandChildDocTemplate(int id) { -int docNum = id / 8; // the index of docs sent to solr in the AddUpdateCommand. e.g. first doc is 0 +int docNum = (id / sumOfDocsPerNestedDocument) % numberOfDocsPerNestedTest; // the index of docs sent to solr in the AddUpdateCommand. e.g. first doc is 0 --- End diff -- This is looking kinda complicated now (same for fullNestedDocTemplate). Does it really matter how the type_s value is chosen exactly? I don't know; I confess to have glossed over this aspect of the test; I don't get the point. I wonder if whatever the test is trying to truly test here might be simplified to go about it in some other means. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r211108109 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -87,7 +87,12 @@ public void transform(SolrDocument rootDoc, int rootDocId) { final int segBaseId = leafReaderContext.docBase; final int segRootId = rootDocId - segBaseId; final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); - final int segPrevRootId = segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + final int segPrevRootId = rootDocId==0? -1: segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + if(segPrevRootId == (rootDocId - 1)) { --- End diff -- Yes, I have added this conditional to the test. Let's hope I did not misread your intentions :). --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210600024 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -87,7 +87,12 @@ public void transform(SolrDocument rootDoc, int rootDocId) { final int segBaseId = leafReaderContext.docBase; final int segRootId = rootDocId - segBaseId; final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); - final int segPrevRootId = segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + final int segPrevRootId = rootDocId==0? -1: segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + if(segPrevRootId == (rootDocId - 1)) { --- End diff -- Ooooh, good catch. Lets enhance the tests in this file a bit to help us give confidence that we're using docIDs correctly (and help avoid future enhancers/modifiers from introducing similar bugs). Here's what I propose: in the @BeforeClass, if random().nextBoolean(), add some nested docs -- using one of your existing nested document adding methods. And randomly do a commit() to flush the segment. Later in the test methods we need to add a filter query that will exclude those docs. One way to do this is to ensure these first docs have some field we can exclude. Another way might be knowing the maximum uniqueKey ID you can query by prior to the test starting, and then adding a filter query with a range saying the uniqueKey must be at least this value. Make sense? --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210482225 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -87,7 +87,12 @@ public void transform(SolrDocument rootDoc, int rootDocId) { final int segBaseId = leafReaderContext.docBase; final int segRootId = rootDocId - segBaseId; final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); - final int segPrevRootId = segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + final int segPrevRootId = rootDocId==0? -1: segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + if(segPrevRootId == (rootDocId - 1)) { --- End diff -- If segRootId is 0, segParentsBitSet.prevSetBit(-1) throws an assertion error, since the index has to be >= 0. I will fix line 92 --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210478700 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -87,7 +87,12 @@ public void transform(SolrDocument rootDoc, int rootDocId) { final int segBaseId = leafReaderContext.docBase; final int segRootId = rootDocId - segBaseId; final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); - final int segPrevRootId = segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + final int segPrevRootId = rootDocId==0? -1: segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + if(segPrevRootId == (rootDocId - 1)) { --- End diff -- You altered line 90 in response to my comment. I'm referring to line 92 -- `if(segPrevRootId == (rootDocId - 1))`, where my comment is. Interesting though line 90 is different from the line of code I committed to the feature branch. That line on the feature branch is: `final int segPrevRootId = segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay` Notice there is no conditional, but there is in your version in this PR. BitSet.prevSetBit will return -1 if given a 0 input (so says it's documentation). That's what I was trying to say in my comment on the line of code. Why did you change it? --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210475699 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -291,4 +302,15 @@ private static String generateDocHierarchy(int i) { "}\n" + "}"; } + + private static String IndexWoChildDocs() { --- End diff -- Oops how embarrassing, guess I was in a hurry :(. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210322853 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -291,4 +302,15 @@ private static String generateDocHierarchy(int i) { "}\n" + "}"; } + + private static String IndexWoChildDocs() { --- End diff -- First, never start a method name with an uppercase letter (at least in Java). But secondly, I suggest inlining this to the point of use since you're only using it in one place. I know this is a stylistic point but I find it harder to read code that refers to a bunch of other things in other places that I have to go read -- it's disjointed; disrupts reading flow. Of course a good deal of that is normal (calling string.whatever) but here it's only used for this test. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210322181 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -87,7 +87,12 @@ public void transform(SolrDocument rootDoc, int rootDocId) { final int segBaseId = leafReaderContext.docBase; final int segRootId = rootDocId - segBaseId; final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); - final int segPrevRootId = segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + final int segPrevRootId = rootDocId==0? -1: segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + if(segPrevRootId == (rootDocId - 1)) { --- End diff -- you are comparing a segment local ID with a global ID which is incorrect. You should refer to segRootId. This is why I'm particular about using "seg" nomenclature in a body of code that deals with both segment and global IDs -- it makes it at least easier to identify such an error. It's difficult to get tests to detect this; we'd need to commit some docs up front to cause more segments to be created than many tests will do. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210309845 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -227,27 +225,28 @@ private static String getPathByDocId(int segDocId, SortedDocValues segPathDocVal /** * - * @param segDocBaseId base docID of the segment - * @param RootId docID if the current root document - * @param lastDescendantId lowest docID of the root document's descendant - * @return the docID to loop and to not surpass limit of descendants to match specified by query + * @param RootDocId docID if the current root document + * @param lowestChildDocId lowest docID of the root document's descendant + * @return the docID to loop and not surpass limit of descendants to match specified by query */ - private int calcLimitIndex(int segDocBaseId, int RootId, int lastDescendantId) { -int i = segDocBaseId + RootId - 1; // the child document with the highest docID -final int prevSegRootId = segDocBaseId + lastDescendantId; -assert prevSegRootId < i; // previous rootId should be smaller then current RootId + private int calcDocIdToIterateFrom(int lowestChildDocId, int RootDocId) { +assert lowestChildDocId < RootDocId; // first childDocId should be smaller then current RootId --- End diff -- Yes it would, I'll add a test to ensure its behaviour. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210306003 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -227,27 +225,28 @@ private static String getPathByDocId(int segDocId, SortedDocValues segPathDocVal /** * - * @param segDocBaseId base docID of the segment - * @param RootId docID if the current root document - * @param lastDescendantId lowest docID of the root document's descendant - * @return the docID to loop and to not surpass limit of descendants to match specified by query + * @param RootDocId docID if the current root document + * @param lowestChildDocId lowest docID of the root document's descendant + * @return the docID to loop and not surpass limit of descendants to match specified by query */ - private int calcLimitIndex(int segDocBaseId, int RootId, int lastDescendantId) { -int i = segDocBaseId + RootId - 1; // the child document with the highest docID -final int prevSegRootId = segDocBaseId + lastDescendantId; -assert prevSegRootId < i; // previous rootId should be smaller then current RootId + private int calcDocIdToIterateFrom(int lowestChildDocId, int RootDocId) { +assert lowestChildDocId < RootDocId; // first childDocId should be smaller then current RootId --- End diff -- if the root doc has no children, this assertion would fail, right? Hmm; should be tested we don't blow up/fail. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210294082 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -224,9 +225,29 @@ private static String getPathByDocId(int segDocId, SortedDocValues segPathDocVal return segPathDocValues.binaryValue().utf8ToString(); } - private static String getSolrFieldString(Object fieldVal, FieldType fieldType) { -return fieldVal instanceof IndexableField -? fieldType.toExternal((IndexableField)fieldVal) -: fieldVal.toString(); + /** + * + * @param segDocBaseId base docID of the segment + * @param RootId docID if the current root document + * @param lastDescendantId lowest docID of the root document's descendant + * @return the docID to loop and to not surpass limit of descendants to match specified by query + */ + private int calcLimitIndex(int segDocBaseId, int RootId, int lastDescendantId) { +int i = segDocBaseId + RootId - 1; // the child document with the highest docID --- End diff -- Changed and noted :) --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210291464 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -81,8 +81,8 @@ private void testChildDoctransformerXML() { String test3[] = new String[] { "//*[@numFound='1']", -"/response/result/doc[1]/doc[1]/str[@name='id']='3'" , -"/response/result/doc[1]/doc[2]/str[@name='id']='5'" }; +"/response/result/doc[1]/doc[1]/str[@name='id']='5'" , --- End diff -- The next sentence is a proposal for a different way to limit the number of child documents. Incase the current logic is sufficient, then we have no need to worry about it. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210290075 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -81,8 +81,8 @@ private void testChildDoctransformerXML() { String test3[] = new String[] { "//*[@numFound='1']", -"/response/result/doc[1]/doc[1]/str[@name='id']='3'" , -"/response/result/doc[1]/doc[2]/str[@name='id']='5'" }; +"/response/result/doc[1]/doc[1]/str[@name='id']='5'" , --- End diff -- "If we have 3 child docs" -- the explanation in that sentence is perfectly clear, I think. The next sentence confused me, but whatever. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210288829 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -81,8 +81,8 @@ private void testChildDoctransformerXML() { String test3[] = new String[] { "//*[@numFound='1']", -"/response/result/doc[1]/doc[1]/str[@name='id']='3'" , -"/response/result/doc[1]/doc[2]/str[@name='id']='5'" }; +"/response/result/doc[1]/doc[1]/str[@name='id']='5'" , --- End diff -- I guess I was not specific enough beforehand. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210287119 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -81,8 +81,8 @@ private void testChildDoctransformerXML() { String test3[] = new String[] { "//*[@numFound='1']", -"/response/result/doc[1]/doc[1]/str[@name='id']='3'" , -"/response/result/doc[1]/doc[2]/str[@name='id']='5'" }; +"/response/result/doc[1]/doc[1]/str[@name='id']='5'" , --- End diff -- since we initialize the document using from the highest index to exhaust the limit if possible, it means some documents may be skipped. If we have 3 child docs: 1 , 2, 3, and the limit is set to "2", only doc 2 and 3 will be added if we initialise the index to the one that will exhaust the limit. Another way is simply to count the number of matching docs so far, and continue onto the next root doc if reached(return from the transform method). --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210287034 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -81,8 +81,8 @@ private void testChildDoctransformerXML() { String test3[] = new String[] { "//*[@numFound='1']", -"/response/result/doc[1]/doc[1]/str[@name='id']='3'" , -"/response/result/doc[1]/doc[2]/str[@name='id']='5'" }; +"/response/result/doc[1]/doc[1]/str[@name='id']='5'" , --- End diff -- Again, the order hasn't changed ;-) --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210285891 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -81,8 +81,8 @@ private void testChildDoctransformerXML() { String test3[] = new String[] { "//*[@numFound='1']", -"/response/result/doc[1]/doc[1]/str[@name='id']='3'" , -"/response/result/doc[1]/doc[2]/str[@name='id']='5'" }; +"/response/result/doc[1]/doc[1]/str[@name='id']='5'" , --- End diff -- Yes, I meant the order in which the documents are counted. My bad. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210280357 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -81,8 +81,8 @@ private void testChildDoctransformerXML() { String test3[] = new String[] { "//*[@numFound='1']", -"/response/result/doc[1]/doc[1]/str[@name='id']='3'" , -"/response/result/doc[1]/doc[2]/str[@name='id']='5'" }; +"/response/result/doc[1]/doc[1]/str[@name='id']='5'" , --- End diff -- I suspect you are simply using the "sort" word inappropriately. "sort" is related to ordering, but we didn't change the order. We did change the "window" or "offset", if you will, into the docs to examine. Ordering/sort hasn't changed. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210273819 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -0,0 +1,253 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.ReaderUtil; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.util.BitSet; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class ChildDocTransformer extends DocTransformer { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final String ANON_CHILD_KEY = "_childDocuments_"; + + private final String name; + private final BitSetProducer parentsFilter; + private final DocSet childDocSet; + private final int limit; + + private final SolrReturnFields childReturnFields = new SolrReturnFields(); + + ChildDocTransformer(String name, BitSetProducer parentsFilter, + DocSet childDocSet, int limit) { +this.name = name; +this.parentsFilter = parentsFilter; +this.childDocSet = childDocSet; +this.limit = limit; + } + + @Override + public String getName() { +return name; + } + + @Override + 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! + +try { + + // lookup what the *previous* rootDocId is, and figure which segment this is + final SolrIndexSearcher searcher = context.getSearcher(); + final List leaves = searcher.getIndexReader().leaves(); + final int seg = ReaderUtil.subIndex(rootDocId, leaves); + final LeafReaderContext leafReaderContext = leaves.get(seg); + final int segBaseId = leafReaderContext.docBase; + final int segRootId = rootDocId - segBaseId; + final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); + final int segPrevRootId = segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + // we'll need this soon... + final SortedDocValues segPathDocValues = DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME); + + // the key in the Map is the document's ancestors key(one above the parent), while the key in the intermediate + // MultiMap is the direct child document's key(of the parent document) + Map> pendingParentPathsToChildren = new HashMap<>(); + + IndexSchema schema = searcher.getSchema(); + SolrDocumentFetcher docFetcher = searcher
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210274031 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -224,9 +225,29 @@ private static String getPathByDocId(int segDocId, SortedDocValues segPathDocVal return segPathDocValues.binaryValue().utf8ToString(); } - private static String getSolrFieldString(Object fieldVal, FieldType fieldType) { -return fieldVal instanceof IndexableField -? fieldType.toExternal((IndexableField)fieldVal) -: fieldVal.toString(); + /** + * + * @param segDocBaseId base docID of the segment + * @param RootId docID if the current root document + * @param lastDescendantId lowest docID of the root document's descendant + * @return the docID to loop and to not surpass limit of descendants to match specified by query + */ + private int calcLimitIndex(int segDocBaseId, int RootId, int lastDescendantId) { +int i = segDocBaseId + RootId - 1; // the child document with the highest docID --- End diff -- I strongly suggest avoiding 'i' as a variable name unless it's a loop index --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210276463 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -83,6 +84,57 @@ public void testParentFilterJSON() throws Exception { tests); } + @Test + public void testParentFilterLimitJSON() throws Exception { +indexSampleData(10); +String[] tests = new String[] { --- End diff -- why define these tests up front? The vast majority of "assertJQ" or similar calls I've seen in Solr's tests put them inline at the method call, which I think makes the most sense since it's together with the query. And can the length be checked here? I think that's a key element of this test's purpose :-) BTW if you use the XML based assertions, you have a richer language to work with -- XPath. The json-like assertJQ is some home-grown thing in this project that supposedly is easier for people to understand due to the json-like nature (industry shifts from XML to JSON) but it's limited in capability. I'm not sure if assertJQ can assert an array length but you could investigate. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210271399 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -81,8 +81,8 @@ private void testChildDoctransformerXML() { String test3[] = new String[] { "//*[@numFound='1']", -"/response/result/doc[1]/doc[1]/str[@name='id']='3'" , -"/response/result/doc[1]/doc[2]/str[@name='id']='5'" }; +"/response/result/doc[1]/doc[1]/str[@name='id']='5'" , --- End diff -- How could choosing the index based on the "limit" change the document ordering? My understanding is that child documents placed onto the parent via this transformer are in docID order, which is the same order they were in as provided to Solr. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210263030 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -0,0 +1,253 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.ReaderUtil; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.util.BitSet; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.search.DocSet; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class ChildDocTransformer extends DocTransformer { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final String ANON_CHILD_KEY = "_childDocuments_"; + + private final String name; + private final BitSetProducer parentsFilter; + private final DocSet childDocSet; + private final int limit; + + private final SolrReturnFields childReturnFields = new SolrReturnFields(); + + ChildDocTransformer(String name, BitSetProducer parentsFilter, + DocSet childDocSet, int limit) { +this.name = name; +this.parentsFilter = parentsFilter; +this.childDocSet = childDocSet; +this.limit = limit; + } + + @Override + public String getName() { +return name; + } + + @Override + 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! + +try { + + // lookup what the *previous* rootDocId is, and figure which segment this is + final SolrIndexSearcher searcher = context.getSearcher(); + final List leaves = searcher.getIndexReader().leaves(); + final int seg = ReaderUtil.subIndex(rootDocId, leaves); + final LeafReaderContext leafReaderContext = leaves.get(seg); + final int segBaseId = leafReaderContext.docBase; + final int segRootId = rootDocId - segBaseId; + final BitSet segParentsBitSet = parentsFilter.getBitSet(leafReaderContext); + final int segPrevRootId = segParentsBitSet.prevSetBit(segRootId - 1); // can return -1 and that's okay + + // we'll need this soon... + final SortedDocValues segPathDocValues = DocValues.getSorted(leafReaderContext.reader(), NEST_PATH_FIELD_NAME); + + // the key in the Map is the document's ancestors key(one above the parent), while the key in the intermediate + // MultiMap is the direct child document's key(of the parent document) + Map> pendingParentPathsToChildren = new HashMap<>(); + + IndexSchema schema = searcher.getSchema(); + SolrDocumentFetcher docFetcher = searche
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r210262386 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -81,8 +81,8 @@ private void testChildDoctransformerXML() { String test3[] = new String[] { "//*[@numFound='1']", -"/response/result/doc[1]/doc[1]/str[@name='id']='3'" , -"/response/result/doc[1]/doc[2]/str[@name='id']='5'" }; +"/response/result/doc[1]/doc[1]/str[@name='id']='5'" , --- End diff -- This had to be changed since we initialise the for loop in ChildDocTransformer using the limit param, so the sort is docID descending now. Thought it is important to point it out, and perhaps this should also be documented. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r209862802 --- Diff: solr/core/src/test-files/solr/collection1/conf/schema15.xml --- @@ -567,7 +567,17 @@ - + + + + --- End diff -- Sure thing --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r209070327 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformer.java --- @@ -0,0 +1,244 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class ChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + private boolean hasPaths; + private boolean anonChildDoc; + + public ChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; +this.hasPaths = req.getSchema().hasExplicitField(NEST_PATH_FIELD_NAME); +this.anonChildDoc = req.getParams().getBool(CommonParams.ANONYMOUS_CHILD_DOCS, false); + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to perform a ChildBlockJoinQuery +return new String[] { idField.getName() }; + } + + public boolean hasPaths() { +return hasPaths; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList chi
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r209069776 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java --- @@ -70,109 +73,59 @@ public DocTransformer create(String field, SolrParams params, SolrQueryRequest r } String parentFilter = params.get( "parentFilter" ); -if( parentFilter == null ) { - throw new SolrException( ErrorCode.BAD_REQUEST, "Parent filter should be sent as parentFilter=filterCondition" ); -} - -String childFilter = params.get( "childFilter" ); -int limit = params.getInt( "limit", 10 ); - BitSetProducer parentsFilter = null; -try { - Query parentFilterQuery = QParser.getParser( parentFilter, req).getQuery(); - //TODO shouldn't we try to use the Solr filter cache, and then ideally implement - // BitSetProducer over that? - // DocSet parentDocSet = req.getSearcher().getDocSet(parentFilterQuery); - // then return BitSetProducer with custom BitSet impl accessing the docSet - parentsFilter = new QueryBitSetProducer(parentFilterQuery); -} catch (SyntaxError syntaxError) { - throw new SolrException( ErrorCode.BAD_REQUEST, "Failed to create correct parent filter query" ); -} - -Query childFilterQuery = null; -if(childFilter != null) { +boolean buildHierarchy = params.getBool("hierarchy", false); +if( parentFilter == null) { + if(!buildHierarchy) { +throw new SolrException( ErrorCode.BAD_REQUEST, "Parent filter should be sent as parentFilter=filterCondition" ); + } + parentsFilter = new QueryBitSetProducer(rootFilter); +} else { try { -childFilterQuery = QParser.getParser( childFilter, req).getQuery(); +Query parentFilterQuery = QParser.getParser(parentFilter, req).getQuery(); +//TODO shouldn't we try to use the Solr filter cache, and then ideally implement +// BitSetProducer over that? +// DocSet parentDocSet = req.getSearcher().getDocSet(parentFilterQuery); +// then return BitSetProducer with custom BitSet impl accessing the docSet +parentsFilter = new QueryBitSetProducer(parentFilterQuery); } catch (SyntaxError syntaxError) { -throw new SolrException( ErrorCode.BAD_REQUEST, "Failed to create correct child filter query" ); +throw new SolrException( ErrorCode.BAD_REQUEST, "Failed to create correct parent filter query" ); } } -return new ChildDocTransformer( field, parentsFilter, uniqueKeyField, req.getSchema(), childFilterQuery, limit); - } -} - -class ChildDocTransformer extends DocTransformer { - private final String name; - private final SchemaField idField; - private final IndexSchema schema; - private BitSetProducer parentsFilter; - private Query childFilterQuery; - private int limit; - - public ChildDocTransformer( String name, final BitSetProducer parentsFilter, - final SchemaField idField, IndexSchema schema, - final Query childFilterQuery, int limit) { -this.name = name; -this.idField = idField; -this.schema = schema; -this.parentsFilter = parentsFilter; -this.childFilterQuery = childFilterQuery; -this.limit = limit; - } +String childFilter = params.get( "childFilter" ); +int limit = params.getInt( "limit", 10 ); - @Override - public String getName() { -return name; - } - - @Override - public String[] getExtraRequestFields() { -// we always need the idField (of the parent) in order to fill out it's children -return new String[] { idField.getName() }; +if(buildHierarchy) { + if(childFilter != null) { +childFilter = buildHierarchyChildFilterString(childFilter); +return new ChildDocTransformer(field, parentsFilter, req, +getChildQuery(childFilter, req), limit); + } + return new ChildDocTransformer(field, parentsFilter, req, null, limit); +} +return new ChildDocTransformer( field, parentsFilter, req, +childFilter==null? null: getChildQuery(childFilter, req), limit); } - @Override - public void transform(SolrDocument doc, int docid) { - -FieldType idFt = idField.getType(); -Object parentIdField = doc.getFirstValue(idField.getName()); - -String parentIdExt = parentIdField instanceof IndexableField - ? idFt.toExternal((IndexableField)parentIdField) - : parentIdField.toString(); - + private stati
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r209066014 --- Diff: solr/core/src/test/org/apache/solr/update/TestNestedUpdateProcessor.java --- @@ -107,8 +107,8 @@ public void testDeeplyNestedURPGrandChild() throws Exception { }; indexSampleData(jDoc); -assertJQ(req("q", IndexSchema.NEST_PATH_FIELD_NAME + ":*/grandChild#*", -"fl","*", +assertJQ(req("q", IndexSchema.NEST_PATH_FIELD_NAME + ":*/grandChild", +"fl","*, _nest_path_", --- End diff -- This change (and others here) to a test of an URP that isn't modified in this issue underscores my point made previously having the test for that URP be more of a unit test of what the URP produces (test the SolrInputDocument), and _not_ executing queries. I'm not saying it was wrong to make this change in this issue but just want you to reflect on the ramifications of these choices. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r209071109 --- Diff: solr/core/src/test-files/solr/collection1/conf/schema15.xml --- @@ -567,7 +567,17 @@ - + + + + --- End diff -- Can you please provide an example input String (here in GH) that has multiple levels and comment out it looks like when it's done? I know how to read regexps but I need to stare at them a bit to figure them out, so lets make it easier to read/review. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r209067568 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -0,0 +1,249 @@ +/* + * 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.response.transform; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import com.google.common.collect.Iterables; +import org.apache.lucene.index.IndexableField; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.BasicResultContext; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestChildDocTransformerHierarchy extends SolrTestCaseJ4 { --- End diff -- I'm glad you have split out this test. I want to further create a new "schema-nest.xml" for our tests that explicitly deal with this new nested document stuff. This test will use it, as well as TestNestedUpdateProcessor. schema15.xml can be reverted to as it was. TestChildDocTransformer can continue to use the legacy schema and as-such we can observe that our additions to the underlying code don't disturb anyone who is using it that is unaware of the new nested stuff. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r209070656 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java --- @@ -61,6 +57,13 @@ */ public class ChildDocTransformerFactory extends TransformerFactory { + public static final String PATH_SEP_CHAR = "/"; --- End diff -- I don't see why these are declared as Strings and not `char`. It's clumsy later to do `charAt(0)` --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r209068211 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -242,10 +242,10 @@ private void testChildDocNonStoredDVFields() throws Exception { "fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1); assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ", -"fl", "subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); +"fl", "id,_childDocuments_,subject,intDvoDefault,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); --- End diff -- I'm really not a fan of {{anonChildDocs}} flag; I regret I conjured up the idea. If we have "nest" schema fields, the user wants nested documents (including field/label association), if the schema doesn't it ought to work as it used to. I think this is straight-forward to reason about and document. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r208932604 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -242,10 +242,10 @@ private void testChildDocNonStoredDVFields() throws Exception { "fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1); assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ", -"fl", "subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); +"fl", "id,_childDocuments_,subject,intDvoDefault,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); --- End diff -- Of course, one day this will probably get removed. Right now there is a way to provide the old XML format. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r208928799 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -242,10 +242,10 @@ private void testChildDocNonStoredDVFields() throws Exception { "fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1); assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ", -"fl", "subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); +"fl", "id,_childDocuments_,subject,intDvoDefault,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); --- End diff -- I think either the user hasn't yet started using the new key'ed/labeled style of child documents, or they have updated completely. It's a migration to a new way you either do or don't do (and perhaps one day will not have a choice). --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r208813471 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -242,10 +242,10 @@ private void testChildDocNonStoredDVFields() throws Exception { "fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1); assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ", -"fl", "subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); +"fl", "id,_childDocuments_,subject,intDvoDefault,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); --- End diff -- I was thinking about what you said, and perhaps we could add the old format using if the user did not index the field meta-data. I prefer having a flag, since the user can query separate two collections and get the same output if desired, instead of having to deal with two different XML formats. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r208813169 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -242,10 +242,10 @@ private void testChildDocNonStoredDVFields() throws Exception { "fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1); assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ", -"fl", "subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); +"fl", "id,_childDocuments_,subject,intDvoDefault,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); --- End diff -- I had to add a small addition and indeed, the behaviour was as expected. I added a test to ensure there is backwards compatibility with the old XML format [here](https://github.com/apache/lucene-solr/blob/2573b89e465de615623084145cab7d17e9cb8a07/solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java#L100). --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r208807039 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -242,10 +242,10 @@ private void testChildDocNonStoredDVFields() throws Exception { "fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1); assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ", -"fl", "subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); +"fl", "id,_childDocuments_,subject,intDvoDefault,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); --- End diff -- This is what I would expect would happen. I am going to try it to check this works as expected. Would not want to suddenly change this for everyone :worried: --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r208799313 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -242,10 +242,10 @@ private void testChildDocNonStoredDVFields() throws Exception { "fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1); assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ", -"fl", "subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); +"fl", "id,_childDocuments_,subject,intDvoDefault,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); --- End diff -- ah; interesting. It's logical. Is this only needed for anonymous child docs (thus \_childDocuments\_ or any/all possible relationship names that aren't necessarily just at the root level but anywhere in the hierarchy? Perhaps this is where that "anonChildDocs" ought to come into play again for backwards-compatibility sake? Well perhaps not... someone who is using anonymous child docs today will not have the nested field metadata and thus the old logic will kick in and ensure child documents are added as it was; right? --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r208507249 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformerHierarchy.java --- @@ -32,7 +32,7 @@ import org.junit.BeforeClass; import org.junit.Test; -public class TestDeeplyNestedChildDocTransformer extends SolrTestCaseJ4 { +public class TestChildDocTransformerHierarchy extends SolrTestCaseJ4 { --- End diff -- These "hierarchy" tests are in a separate class since they require a different schema and configuration to be loaded upon start-up. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r208507038 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -242,10 +242,10 @@ private void testChildDocNonStoredDVFields() throws Exception { "fl", "*,[child parentFilter=\"subject:parentDocument\"]"), test1); assertJQ(req("q", "*:*", "fq", "subject:\"parentDocument\" ", -"fl", "subject,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); +"fl", "id,_childDocuments_,subject,intDvoDefault,[child parentFilter=\"subject:parentDocument\" childFilter=\"title:foo\"]"), test2); --- End diff -- Another major difference is that now since child documents are stored as fields, the user needs to explicitly add them to the list of return fields. I have no opinion regarding this issue, but this might pose a problem to some use cases. Perhaps documenting this would be enough? --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r208506508 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestChildDocTransformer.java --- @@ -66,34 +66,34 @@ public void testAllParams() throws Exception { private void testChildDoctransformerXML() { String test1[] = new String[] { "//*[@numFound='1']", -"/response/result/doc[1]/doc[1]/str[@name='id']='2'" , -"/response/result/doc[1]/doc[2]/str[@name='id']='3'" , -"/response/result/doc[1]/doc[3]/str[@name='id']='4'" , -"/response/result/doc[1]/doc[4]/str[@name='id']='5'" , -"/response/result/doc[1]/doc[5]/str[@name='id']='6'" , -"/response/result/doc[1]/doc[6]/str[@name='id']='7'"}; + "/response/result/doc[1]/arr[@name='_childDocuments_']/doc[1]/str[@name='id']='2'" , --- End diff -- One major difference is the way nested XML is returned, since now it has a key it belongs to. Thought it would make the review process easier for you if I pointed these differences out. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r206901995 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestDeeplyNestedChildDocTransformer.java --- @@ -168,35 +172,57 @@ private static String id() { return "" + counter.incrementAndGet(); } + private static void cleanSolrDocumentFields(SolrDocument input) { +for(Map.Entry field: input) { + Object val = field.getValue(); + if(val instanceof Collection) { +Object newVals = ((Collection) val).stream().map((item) -> (cleanIndexableField(item))) +.collect(Collectors.toList()); +input.setField(field.getKey(), newVals); +continue; + } else { +input.setField(field.getKey(), cleanIndexableField(field.getValue())); + } +} + } + + private static Object cleanIndexableField(Object field) { +if(field instanceof IndexableField) { + return ((IndexableField) field).stringValue(); +} else if(field instanceof SolrDocument) { + cleanSolrDocumentFields((SolrDocument) field); +} +return field; + } + private static String grandChildDocTemplate(int id) { int docNum = id / 8; // the index of docs sent to solr in the AddUpdateCommand. e.g. first doc is 0 -return "SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, type_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">, " + - "toppings=[SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, type_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + id + ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">, " + - "ingredients=[SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], " + - "_nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + (id + 3) + ">, _root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">}]}, " + - "SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, type_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + id + ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">, " + - "ingredients=[SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + (id + 5)+ ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">}, " + - "SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + (id + 5) + ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">}]}]}"; +return "SolrDocument{id="+ id + ", type_s=[" + types[docNum % types.length] + "], name_s=[" + names[docNum % names.length] + "], " + --- End diff -- Keeping one ID is fine; we certainly don't need additional ones. Maybe consider using letters or names for IDs instead of incrementing counters. Anything to help make reading a doc/child structure more readily apparent. Anything to reduce string interpolation here is also a win IMO. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r206892630 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { --- End diff -- Yes; it can do both easily enough I think? A separate method could take over for the existing/legacy case. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r206846562 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { --- End diff -- ? --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r206184346 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { --- End diff -- Would the transformer need to support the old method of adding childDocuments to the _childDocuments_ field? --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r206034965 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestDeeplyNestedChildDocTransformer.java --- @@ -168,35 +172,57 @@ private static String id() { return "" + counter.incrementAndGet(); } + private static void cleanSolrDocumentFields(SolrDocument input) { +for(Map.Entry field: input) { + Object val = field.getValue(); + if(val instanceof Collection) { +Object newVals = ((Collection) val).stream().map((item) -> (cleanIndexableField(item))) +.collect(Collectors.toList()); +input.setField(field.getKey(), newVals); +continue; + } else { +input.setField(field.getKey(), cleanIndexableField(field.getValue())); + } +} + } + + private static Object cleanIndexableField(Object field) { +if(field instanceof IndexableField) { + return ((IndexableField) field).stringValue(); +} else if(field instanceof SolrDocument) { + cleanSolrDocumentFields((SolrDocument) field); +} +return field; + } + private static String grandChildDocTemplate(int id) { int docNum = id / 8; // the index of docs sent to solr in the AddUpdateCommand. e.g. first doc is 0 -return "SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, type_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">, " + - "toppings=[SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, type_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + id + ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">, " + - "ingredients=[SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], " + - "_nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + (id + 3) + ">, _root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">}]}, " + - "SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, type_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + id + ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">, " + - "ingredients=[SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + (id + 5)+ ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">}, " + - "SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + (id + 5) + ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">}]}]}"; +return "SolrDocument{id="+ id + ", type_s=[" + types[docNum % types.length] + "], name_s=[" + names[docNum % names.length] + "], " + --- End diff -- Perhaps we should only leave ID? I would prefer to have one unique key to make sure the documents are for sure placed under the right parent. Hopefully that will clean most of the noise. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r206012956 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestDeeplyNestedChildDocTransformer.java --- @@ -168,35 +172,57 @@ private static String id() { return "" + counter.incrementAndGet(); } + private static void cleanSolrDocumentFields(SolrDocument input) { +for(Map.Entry field: input) { + Object val = field.getValue(); + if(val instanceof Collection) { +Object newVals = ((Collection) val).stream().map((item) -> (cleanIndexableField(item))) +.collect(Collectors.toList()); +input.setField(field.getKey(), newVals); +continue; + } else { +input.setField(field.getKey(), cleanIndexableField(field.getValue())); + } +} + } + + private static Object cleanIndexableField(Object field) { +if(field instanceof IndexableField) { + return ((IndexableField) field).stringValue(); +} else if(field instanceof SolrDocument) { + cleanSolrDocumentFields((SolrDocument) field); +} +return field; + } + private static String grandChildDocTemplate(int id) { int docNum = id / 8; // the index of docs sent to solr in the AddUpdateCommand. e.g. first doc is 0 -return "SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, type_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">, " + - "toppings=[SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, type_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + id + ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">, " + - "ingredients=[SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], " + - "_nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + (id + 3) + ">, _root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">}]}, " + - "SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, type_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + id + ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">, " + - "ingredients=[SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + (id + 5)+ ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">}, " + - "SolrDocument{id=stored,indexed,tokenized,omitNorms,indexOptions=DOCS, name_s=[stored,indexed,tokenized,omitNorms,indexOptions=DOCS], _nest_parent_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_nest_parent_:" + (id + 5) + ">, " + - "_root_=stored,indexed,tokenized,omitNorms,indexOptions=DOCS<_root_:" + id + ">}]}]}"; +return "SolrDocument{id="+ id + ", type_s=[" + types[docNum % types.length] + "], name_s=[" + names[docNum % names.length] + "], " + --- End diff -- It's a shame to have all this embedded ID calculation business. During cleaning can they be removed (both "id" and nest parent and root) and we still have enough distinguishing characteristics of the docs to know which is which? Seems that way. It adds a lot of noise. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r206011853 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; --- End diff -- Oh right; I forgot we needed an ID to do the child doc query limited by this parent ID. Please add a comment. I don't think we should bother with root. I guess ChildDocTransformer might break if the id field is not stored but did have docValues. That's a shame; it deserves a TODO. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r206011552 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { --- End diff -- Replace/update ChildDocTransformer, I think. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205965045 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; --- End diff -- Perhaps since _root_ is added to every document, we could use that field instead of the ID field? This is just a thought that popped into my head. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205962085 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestDeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,227 @@ +/* + * 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.response.transform; + +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicInteger; + +import com.google.common.collect.Iterables; +import org.apache.lucene.document.StoredField; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.BasicResultContext; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestDeeplyNestedChildDocTransformer extends SolrTestCaseJ4 { + + private static AtomicInteger counter = new AtomicInteger(); + private static final char PATH_SEP_CHAR = '/'; + private static final String[] types = {"donut", "cake"}; + private static final String[] ingredients = {"flour", "cocoa", "vanilla"}; + private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); + private static final String[] names = {"Yaz", "Jazz", "Costa"}; + + @BeforeClass + public static void beforeClass() throws Exception { +initCore("solrconfig-update-processor-chains.xml", "schema15.xml"); + } + + @After + public void after() throws Exception { +assertU(delQ("*:*")); +assertU(commit()); +counter.set(0); // reset id counter + } + + @Test + public void testParentFilterJSON() throws Exception { +indexSampleData(10); +String[] tests = new String[] { +"/response/docs/[0]/type_s==[donut]", +"/response/docs/[0]/toppings/[0]/type_s==[Regular]", +"/response/docs/[0]/toppings/[1]/type_s==[Chocolate]", +"/response/docs/[0]/toppings/[0]/ingredients/[0]/name_s==[cocoa]", +"/response/docs/[0]/toppings/[1]/ingredients/[1]/name_s==[cocoa]", +"/response/docs/[0]/lonely/test_s==[testing]", +"/response/docs/[0]/lonely/lonelyGrandChild/test2_s==[secondTest]", +}; + +try(SolrQueryRequest req = req("q", "type_s:donut", "sort", "id asc", "fl", "*, _nest_path_, [child hierarchy=true]")) { + BasicResultContext res = (BasicResultContext) h.queryAndResponse("/select", req).getResponse(); + Iterator docsStreamer = res.getProcessedDocuments(); + while (docsStreamer.hasNext()) { +SolrDocument doc = docsStreamer.next(); +int currDocId = Integer.parseInt(((StoredField) doc.getFirstValue("id")).stringValue()); +assertEquals("queried docs are not equal to expected output for id: " + currDocId, fullNestedDocTemplate(currDocId), doc.toString()); + } +} + +assertJQ(req("q", "type_s:donut", +"sort", "id asc", +"fl", "*, _nest_path_, [child hierarchy=true]"), +tests); + } + + @Test + public void testExactPath() throws Exception { +indexSampleData(2); +String[] tests = { +"/response/numFound==4", +"/response/docs/[0]/_nest_path_=='toppings#0'", +"/response/docs/[1]/_nest_path_=='toppings#0'", +"/response/docs/[2]/_nest_path_=='toppings#1'", +"/response/docs/[3]/_nest_path_=='toppings#1'", +}; + +assertJQ(req("q", "_nest_path_:*toppings/", +"sort", "_nest_path_ asc", +"fl", "*, _nest_path_"), +tests); + +assertJQ(req("q", "+_nest_path_:\"toppings/\"", +"sort", "_nest_path_ asc", +"fl", "*, _nest_path_"), +tests); + } + + @Test + public void testChildFilterJSON() throws Exception { +indexSampleData(10); +String[] tests = new String[] { +
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205961339 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; --- End diff -- FieldType idFt = idField.getType(); String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); Doesn't this mean we need the root document's ID? --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205960639 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList children = searcher.getDocList(query, childFilterQuery, docKeySort, 0, limit); + long segAndId = searcher.lookupId(new BytesRef(rootIdExt)); + final int seg = (int) (segAndId >> 32); + final LeafReaderContext leafReaderContext = searcher.getIndexReader().leaves().get(seg); + final SortedDocValues segPa
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205960593 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { --- End diff -- Should I just delete the old transformer and make this one the new ChildDocTransformer? Or should I give it a new name like ChildDocHierarchyTransformer? --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205851891 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestDeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,227 @@ +/* + * 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.response.transform; + +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicInteger; + +import com.google.common.collect.Iterables; +import org.apache.lucene.document.StoredField; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.BasicResultContext; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestDeeplyNestedChildDocTransformer extends SolrTestCaseJ4 { + + private static AtomicInteger counter = new AtomicInteger(); + private static final char PATH_SEP_CHAR = '/'; + private static final String[] types = {"donut", "cake"}; + private static final String[] ingredients = {"flour", "cocoa", "vanilla"}; + private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); + private static final String[] names = {"Yaz", "Jazz", "Costa"}; + + @BeforeClass + public static void beforeClass() throws Exception { +initCore("solrconfig-update-processor-chains.xml", "schema15.xml"); + } + + @After + public void after() throws Exception { +assertU(delQ("*:*")); +assertU(commit()); +counter.set(0); // reset id counter + } + + @Test + public void testParentFilterJSON() throws Exception { +indexSampleData(10); +String[] tests = new String[] { +"/response/docs/[0]/type_s==[donut]", +"/response/docs/[0]/toppings/[0]/type_s==[Regular]", +"/response/docs/[0]/toppings/[1]/type_s==[Chocolate]", +"/response/docs/[0]/toppings/[0]/ingredients/[0]/name_s==[cocoa]", +"/response/docs/[0]/toppings/[1]/ingredients/[1]/name_s==[cocoa]", +"/response/docs/[0]/lonely/test_s==[testing]", +"/response/docs/[0]/lonely/lonelyGrandChild/test2_s==[secondTest]", +}; + +try(SolrQueryRequest req = req("q", "type_s:donut", "sort", "id asc", "fl", "*, _nest_path_, [child hierarchy=true]")) { + BasicResultContext res = (BasicResultContext) h.queryAndResponse("/select", req).getResponse(); + Iterator docsStreamer = res.getProcessedDocuments(); + while (docsStreamer.hasNext()) { +SolrDocument doc = docsStreamer.next(); +int currDocId = Integer.parseInt(((StoredField) doc.getFirstValue("id")).stringValue()); +assertEquals("queried docs are not equal to expected output for id: " + currDocId, fullNestedDocTemplate(currDocId), doc.toString()); + } +} + +assertJQ(req("q", "type_s:donut", +"sort", "id asc", +"fl", "*, _nest_path_, [child hierarchy=true]"), +tests); + } + + @Test + public void testExactPath() throws Exception { +indexSampleData(2); +String[] tests = { +"/response/numFound==4", +"/response/docs/[0]/_nest_path_=='toppings#0'", +"/response/docs/[1]/_nest_path_=='toppings#0'", +"/response/docs/[2]/_nest_path_=='toppings#1'", +"/response/docs/[3]/_nest_path_=='toppings#1'", +}; + +assertJQ(req("q", "_nest_path_:*toppings/", +"sort", "_nest_path_ asc", +"fl", "*, _nest_path_"), +tests); + +assertJQ(req("q", "+_nest_path_:\"toppings/\"", +"sort", "_nest_path_ asc", +"fl", "*, _nest_path_"), +tests); + } + + @Test + public void testChildFilterJSON() throws Exception { +indexSampleData(10); +String[] tests = new String[] { +
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205468112 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList children = searcher.getDocList(query, childFilterQuery, docKeySort, 0, limit); + long segAndId = searcher.lookupId(new BytesRef(rootIdExt)); + final int seg = (int) (segAndId >> 32); + final LeafReaderContext leafReaderContext = searcher.getIndexReader().leaves().get(seg); + final SortedDocValues segPat
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205488727 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestDeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,227 @@ +/* + * 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.response.transform; + +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicInteger; + +import com.google.common.collect.Iterables; +import org.apache.lucene.document.StoredField; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.BasicResultContext; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestDeeplyNestedChildDocTransformer extends SolrTestCaseJ4 { + + private static AtomicInteger counter = new AtomicInteger(); + private static final char PATH_SEP_CHAR = '/'; + private static final String[] types = {"donut", "cake"}; + private static final String[] ingredients = {"flour", "cocoa", "vanilla"}; + private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); + private static final String[] names = {"Yaz", "Jazz", "Costa"}; + + @BeforeClass + public static void beforeClass() throws Exception { +initCore("solrconfig-update-processor-chains.xml", "schema15.xml"); + } + + @After + public void after() throws Exception { +assertU(delQ("*:*")); +assertU(commit()); +counter.set(0); // reset id counter + } + + @Test + public void testParentFilterJSON() throws Exception { +indexSampleData(10); +String[] tests = new String[] { +"/response/docs/[0]/type_s==[donut]", +"/response/docs/[0]/toppings/[0]/type_s==[Regular]", +"/response/docs/[0]/toppings/[1]/type_s==[Chocolate]", +"/response/docs/[0]/toppings/[0]/ingredients/[0]/name_s==[cocoa]", +"/response/docs/[0]/toppings/[1]/ingredients/[1]/name_s==[cocoa]", +"/response/docs/[0]/lonely/test_s==[testing]", +"/response/docs/[0]/lonely/lonelyGrandChild/test2_s==[secondTest]", +}; + +try(SolrQueryRequest req = req("q", "type_s:donut", "sort", "id asc", "fl", "*, _nest_path_, [child hierarchy=true]")) { + BasicResultContext res = (BasicResultContext) h.queryAndResponse("/select", req).getResponse(); + Iterator docsStreamer = res.getProcessedDocuments(); + while (docsStreamer.hasNext()) { +SolrDocument doc = docsStreamer.next(); +int currDocId = Integer.parseInt(((StoredField) doc.getFirstValue("id")).stringValue()); +assertEquals("queried docs are not equal to expected output for id: " + currDocId, fullNestedDocTemplate(currDocId), doc.toString()); + } +} + +assertJQ(req("q", "type_s:donut", +"sort", "id asc", +"fl", "*, _nest_path_, [child hierarchy=true]"), +tests); + } + + @Test + public void testExactPath() throws Exception { +indexSampleData(2); +String[] tests = { +"/response/numFound==4", +"/response/docs/[0]/_nest_path_=='toppings#0'", +"/response/docs/[1]/_nest_path_=='toppings#0'", +"/response/docs/[2]/_nest_path_=='toppings#1'", +"/response/docs/[3]/_nest_path_=='toppings#1'", +}; + +assertJQ(req("q", "_nest_path_:*toppings/", +"sort", "_nest_path_ asc", +"fl", "*, _nest_path_"), +tests); + +assertJQ(req("q", "+_nest_path_:\"toppings/\"", +"sort", "_nest_path_ asc", +"fl", "*, _nest_path_"), +tests); + } + + @Test + public void testChildFilterJSON() throws Exception { +indexSampleData(10); +String[] tests = new String[] { +
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205477055 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList children = searcher.getDocList(query, childFilterQuery, docKeySort, 0, limit); + long segAndId = searcher.lookupId(new BytesRef(rootIdExt)); + final int seg = (int) (segAndId >> 32); + final LeafReaderContext leafReaderContext = searcher.getIndexReader().leaves().get(seg); + final SortedDocValues segPat
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205466774 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { --- End diff -- As with our URP, lets forgo the "Deeply" terminology. I hope this will simply be how any nested docs in the future are done rather than making a distinction. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205475640 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList children = searcher.getDocList(query, childFilterQuery, docKeySort, 0, limit); + long segAndId = searcher.lookupId(new BytesRef(rootIdExt)); + final int seg = (int) (segAndId >> 32); + final LeafReaderContext leafReaderContext = searcher.getIndexReader().leaves().get(seg); + final SortedDocValues segPat
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205475272 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList children = searcher.getDocList(query, childFilterQuery, docKeySort, 0, limit); + long segAndId = searcher.lookupId(new BytesRef(rootIdExt)); + final int seg = (int) (segAndId >> 32); + final LeafReaderContext leafReaderContext = searcher.getIndexReader().leaves().get(seg); + final SortedDocValues segPat
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205480076 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList children = searcher.getDocList(query, childFilterQuery, docKeySort, 0, limit); + long segAndId = searcher.lookupId(new BytesRef(rootIdExt)); + final int seg = (int) (segAndId >> 32); + final LeafReaderContext leafReaderContext = searcher.getIndexReader().leaves().get(seg); + final SortedDocValues segPat
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205482972 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestDeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,227 @@ +/* + * 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.response.transform; + +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicInteger; + +import com.google.common.collect.Iterables; +import org.apache.lucene.document.StoredField; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.BasicResultContext; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestDeeplyNestedChildDocTransformer extends SolrTestCaseJ4 { + + private static AtomicInteger counter = new AtomicInteger(); + private static final char PATH_SEP_CHAR = '/'; + private static final String[] types = {"donut", "cake"}; + private static final String[] ingredients = {"flour", "cocoa", "vanilla"}; + private static final Iterator ingredientsCycler = Iterables.cycle(ingredients).iterator(); + private static final String[] names = {"Yaz", "Jazz", "Costa"}; + + @BeforeClass + public static void beforeClass() throws Exception { +initCore("solrconfig-update-processor-chains.xml", "schema15.xml"); + } + + @After + public void after() throws Exception { +assertU(delQ("*:*")); +assertU(commit()); +counter.set(0); // reset id counter + } + + @Test + public void testParentFilterJSON() throws Exception { +indexSampleData(10); +String[] tests = new String[] { +"/response/docs/[0]/type_s==[donut]", +"/response/docs/[0]/toppings/[0]/type_s==[Regular]", +"/response/docs/[0]/toppings/[1]/type_s==[Chocolate]", +"/response/docs/[0]/toppings/[0]/ingredients/[0]/name_s==[cocoa]", +"/response/docs/[0]/toppings/[1]/ingredients/[1]/name_s==[cocoa]", +"/response/docs/[0]/lonely/test_s==[testing]", +"/response/docs/[0]/lonely/lonelyGrandChild/test2_s==[secondTest]", +}; + +try(SolrQueryRequest req = req("q", "type_s:donut", "sort", "id asc", "fl", "*, _nest_path_, [child hierarchy=true]")) { + BasicResultContext res = (BasicResultContext) h.queryAndResponse("/select", req).getResponse(); + Iterator docsStreamer = res.getProcessedDocuments(); + while (docsStreamer.hasNext()) { +SolrDocument doc = docsStreamer.next(); +int currDocId = Integer.parseInt(((StoredField) doc.getFirstValue("id")).stringValue()); +assertEquals("queried docs are not equal to expected output for id: " + currDocId, fullNestedDocTemplate(currDocId), doc.toString()); + } +} + +assertJQ(req("q", "type_s:donut", +"sort", "id asc", +"fl", "*, _nest_path_, [child hierarchy=true]"), +tests); + } + + @Test + public void testExactPath() throws Exception { +indexSampleData(2); +String[] tests = { +"/response/numFound==4", +"/response/docs/[0]/_nest_path_=='toppings#0'", +"/response/docs/[1]/_nest_path_=='toppings#0'", +"/response/docs/[2]/_nest_path_=='toppings#1'", +"/response/docs/[3]/_nest_path_=='toppings#1'", +}; + +assertJQ(req("q", "_nest_path_:*toppings/", +"sort", "_nest_path_ asc", +"fl", "*, _nest_path_"), +tests); + +assertJQ(req("q", "+_nest_path_:\"toppings/\"", +"sort", "_nest_path_ asc", +"fl", "*, _nest_path_"), +tests); + } + + @Test + public void testChildFilterJSON() throws Exception { +indexSampleData(10); +String[] tests = new String[] { +
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205478036 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList children = searcher.getDocList(query, childFilterQuery, docKeySort, 0, limit); + long segAndId = searcher.lookupId(new BytesRef(rootIdExt)); + final int seg = (int) (segAndId >> 32); + final LeafReaderContext leafReaderContext = searcher.getIndexReader().leaves().get(seg); + final SortedDocValues segPat
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205468966 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList children = searcher.getDocList(query, childFilterQuery, docKeySort, 0, limit); + long segAndId = searcher.lookupId(new BytesRef(rootIdExt)); + final int seg = (int) (segAndId >> 32); + final LeafReaderContext leafReaderContext = searcher.getIndexReader().leaves().get(seg); + final SortedDocValues segPat
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205474348 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList children = searcher.getDocList(query, childFilterQuery, docKeySort, 0, limit); + long segAndId = searcher.lookupId(new BytesRef(rootIdExt)); + final int seg = (int) (segAndId >> 32); + final LeafReaderContext leafReaderContext = searcher.getIndexReader().leaves().get(seg); + final SortedDocValues segPat
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205463666 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java --- @@ -61,6 +71,12 @@ */ public class ChildDocTransformerFactory extends TransformerFactory { + public static final String PATH_SEP_CHAR = "/"; + public static final String NUM_SEP_CHAR = "#"; + private static final BooleanQuery rootFilter = new BooleanQuery.Builder() + .add(new BooleanClause(new MatchAllDocsQuery(), BooleanClause.Occur.MUST)) + .add(new BooleanClause(new WildcardQuery(new Term(NEST_PATH_FIELD_NAME, new BytesRef("*"))), BooleanClause.Occur.MUST_NOT)).build(); --- End diff -- Remember again to use DocValuesExistsQuery --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205467807 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; --- End diff -- Oh? I didn't know we cared at all what the ID is. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205477315 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,224 @@ +/* + * 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.response.transform; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ToChildBlockJoinQuery; +import org.apache.lucene.util.BytesRef; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.DocsStreamer; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.DocIterator; +import org.apache.solr.search.DocList; +import org.apache.solr.search.SolrDocumentFetcher; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SolrReturnFields; + +import static org.apache.solr.response.transform.ChildDocTransformerFactory.NUM_SEP_CHAR; +import static org.apache.solr.response.transform.ChildDocTransformerFactory.PATH_SEP_CHAR; +import static org.apache.solr.schema.IndexSchema.NEST_PATH_FIELD_NAME; + +class DeeplyNestedChildDocTransformer extends DocTransformer { + + private final String name; + protected final SchemaField idField; + protected final SolrQueryRequest req; + protected final IndexSchema schema; + private BitSetProducer parentsFilter; + protected int limit; + private final static Sort docKeySort = new Sort(new SortField(null, SortField.Type.DOC, false)); + private Query childFilterQuery; + + public DeeplyNestedChildDocTransformer(String name, final BitSetProducer parentsFilter, + final SolrQueryRequest req, final Query childFilterQuery, int limit) { +this.name = name; +this.schema = req.getSchema(); +this.idField = this.schema.getUniqueKeyField(); +this.req = req; +this.parentsFilter = parentsFilter; +this.limit = limit; +this.childFilterQuery = childFilterQuery; + } + + @Override + public String getName() { +return name; + } + + @Override + public String[] getExtraRequestFields() { +// we always need the idField (of the parent) in order to fill out it's children +return new String[] { idField.getName() }; + } + + @Override + public void transform(SolrDocument rootDoc, int rootDocId) { + +FieldType idFt = idField.getType(); + +String rootIdExt = getSolrFieldString(rootDoc.getFirstValue(idField.getName()), idFt); + +try { + Query parentQuery = idFt.getFieldQuery(null, idField, rootIdExt); + Query query = new ToChildBlockJoinQuery(parentQuery, parentsFilter); + SolrIndexSearcher searcher = context.getSearcher(); + DocList children = searcher.getDocList(query, childFilterQuery, docKeySort, 0, limit); + long segAndId = searcher.lookupId(new BytesRef(rootIdExt)); + final int seg = (int) (segAndId >> 32); + final LeafReaderContext leafReaderContext = searcher.getIndexReader().leaves().get(seg); + final SortedDocValues segPat
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205465998 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java --- @@ -91,15 +100,37 @@ public DocTransformer create(String field, SolrParams params, SolrQueryRequest r Query childFilterQuery = null; if(childFilter != null) { - try { -childFilterQuery = QParser.getParser( childFilter, req).getQuery(); - } catch (SyntaxError syntaxError) { -throw new SolrException( ErrorCode.BAD_REQUEST, "Failed to create correct child filter query" ); + if(buildHierarchy) { +childFilter = buildHierarchyChildFilterString(childFilter); +return new DeeplyNestedChildDocTransformer(field, parentsFilter, req, +getChildQuery(childFilter, req), limit); } + childFilterQuery = getChildQuery(childFilter, req); +} else if(buildHierarchy) { + return new DeeplyNestedChildDocTransformer(field, parentsFilter, req, null, limit); } return new ChildDocTransformer( field, parentsFilter, uniqueKeyField, req.getSchema(), childFilterQuery, limit); } + + private static Query getChildQuery(String childFilter, SolrQueryRequest req) { +try { + return QParser.getParser( childFilter, req).getQuery(); +} catch (SyntaxError syntaxError) { + throw new SolrException( ErrorCode.BAD_REQUEST, "Failed to create correct child filter query" ); +} + } + + protected static String buildHierarchyChildFilterString(String queryString) { --- End diff -- Remember to provide input/output example. I think this is where the PathHierarchyTokenizer might come into play... and our discussions on the JIRA issue about that hierarchy. Can we table this for now and do in a follow-up issue? (i.e. have no special syntax right now). I'm just concerned the scope of this may be bigger than limited to this doc transformer since presumably users will want to do join queries using this syntax as well. And this touches on how we index this; which is kinda a bigger discussion than all the stuff going on already in this issue. And this'll need to be documented in the Solr Ref Guide well. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205465327 --- Diff: solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java --- @@ -70,36 +86,62 @@ public DocTransformer create(String field, SolrParams params, SolrQueryRequest r } String parentFilter = params.get( "parentFilter" ); -if( parentFilter == null ) { - throw new SolrException( ErrorCode.BAD_REQUEST, "Parent filter should be sent as parentFilter=filterCondition" ); +BitSetProducer parentsFilter = null; +boolean buildHierarchy = params.getBool("hierarchy", false); +if( parentFilter == null) { + if(!buildHierarchy) { +throw new SolrException( ErrorCode.BAD_REQUEST, "Parent filter should be sent as parentFilter=filterCondition" ); + } + parentsFilter = new QueryBitSetProducer(rootFilter); +} else { + try { +Query parentFilterQuery = QParser.getParser(parentFilter, req).getQuery(); +//TODO shouldn't we try to use the Solr filter cache, and then ideally implement +// BitSetProducer over that? +// DocSet parentDocSet = req.getSearcher().getDocSet(parentFilterQuery); +// then return BitSetProducer with custom BitSet impl accessing the docSet +parentsFilter = new QueryBitSetProducer(parentFilterQuery); + } catch (SyntaxError syntaxError) { +throw new SolrException( ErrorCode.BAD_REQUEST, "Failed to create correct parent filter query" ); + } } String childFilter = params.get( "childFilter" ); int limit = params.getInt( "limit", 10 ); -BitSetProducer parentsFilter = null; -try { - Query parentFilterQuery = QParser.getParser( parentFilter, req).getQuery(); - //TODO shouldn't we try to use the Solr filter cache, and then ideally implement - // BitSetProducer over that? - // DocSet parentDocSet = req.getSearcher().getDocSet(parentFilterQuery); - // then return BitSetProducer with custom BitSet impl accessing the docSet - parentsFilter = new QueryBitSetProducer(parentFilterQuery); -} catch (SyntaxError syntaxError) { - throw new SolrException( ErrorCode.BAD_REQUEST, "Failed to create correct parent filter query" ); -} - Query childFilterQuery = null; if(childFilter != null) { --- End diff -- The code flow from here to the end of the method looks very awkward to me. I think the top "if" condition should test for buildHierarchy that we the nested and non-nested cases are clearly separated. Do you think that would be clear? --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user moshebla commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205403130 --- Diff: solr/core/src/test/org/apache/solr/response/transform/TestDeeplyNestedChildDocTransformer.java --- @@ -0,0 +1,163 @@ +/* + * 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.response.transform; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.solr.SolrTestCaseJ4; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestDeeplyNestedChildDocTransformer extends SolrTestCaseJ4 { + + private static AtomicInteger counter = new AtomicInteger(); + private static final char PATH_SEP_CHAR = '/'; + private static final String[] types = {"donut", "cake"}; + private static final String[] ingredients = {"flour", "cocoa", "vanilla"}; + private static final String[] names = {"Yaz", "Jazz", "Costa"}; + + @BeforeClass + public static void beforeClass() throws Exception { +initCore("solrconfig-update-processor-chains.xml", "schema15.xml"); + } + + @After + public void after() throws Exception { +assertU(delQ("*:*")); +assertU(commit()); + } + + @Test + public void testParentFilterJSON() throws Exception { +indexSampleData(10); +String[] tests = new String[] { --- End diff -- I have just updated this test, hopefully it is a lot better now. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org
[GitHub] lucene-solr pull request #416: WIP: SOLR-12519
Github user dsmiley commented on a diff in the pull request: https://github.com/apache/lucene-solr/pull/416#discussion_r205126064 --- Diff: solr/core/src/java/org/apache/solr/response/transform/DeeplyNestedChildDocTransformer.java --- @@ -132,54 +126,49 @@ public void transform(SolrDocument rootDoc, int rootDocId) { // load the doc SolrDocument doc = DocsStreamer.convertLuceneDocToSolrDoc(docFetcher.doc(docId), schema, new SolrReturnFields()); -doc.setField(NEST_PATH_FIELD_NAME, fullDocPath); if (shouldDecorateWithDVs) { docFetcher.decorateDocValueFields(doc, docId, dvFieldsToReturn); } // get parent path // put into pending String parentDocPath = lookupParentPath(fullDocPath); -pendingParentPathsToChildren.put(parentDocPath, doc); // multimap add (won't replace) -// if this path has pending child docs, add them. -if (isAncestor) { - addChildrenToParent(doc, pendingParentPathsToChildren.get(fullDocPath)); - pendingParentPathsToChildren.removeAll(fullDocPath); // no longer pending +if(isAncestor) { + // if this path has pending child docs, add them. + addChildrenToParent(doc, pendingParentPathsToChildren.remove(fullDocPath)); // no longer pending } +pendingParentPathsToChildren.computeIfAbsent(parentDocPath, x -> ArrayListMultimap.create()) +.put(trimIfSingleDoc(getLastPath(fullDocPath)), doc); // multimap add (won't replace) } } // only children of parent remain assert pendingParentPathsToChildren.keySet().size() == 1; -addChildrenToParent(rootDoc, pendingParentPathsToChildren.get(null)); +addChildrenToParent(rootDoc, pendingParentPathsToChildren.remove(null)); } } catch (IOException e) { rootDoc.put(getName(), "Could not fetch child Documents"); } } - void addChildToParent(SolrDocument parent, SolrDocument child, String label) { -// lookup leaf key for these children using path -// depending on the label, add to the parent at the right key/label -// TODO: unfortunately this is the 2nd time we grab the paths for these docs. resolve how? -String trimmedPath = trimSuffixFromPaths(getLastPath(label)); -if (!parent.containsKey(trimmedPath) && (label.contains(NUM_SEP_CHAR) && !label.endsWith(NUM_SEP_CHAR))) { - List list = new ArrayList<>(); - parent.setField(trimmedPath, list); + void addChildrenToParent(SolrDocument parent, Multimap children) { +for(String childLabel: children.keySet()) { --- End diff -- Ah, I see (I didn't look at Multimap's iteration options when I wrote that). Your code here is good. --- - To unsubscribe, e-mail: dev-unsubscr...@lucene.apache.org For additional commands, e-mail: dev-h...@lucene.apache.org