Sharding is not directly supported, though is not too
difficult, if you can merge the top groups and top documents per
group yourself.
@@ -101,14 +83,14 @@ But the downside is it's somewhat slower to run, and requires more RAM
(using the {@link org.apache.lucene.search.CachingCollector}):
- FirstPassGroupingCollector c1 = new FirstPassGroupingCollector("author", groupSort, groupOffset+topNGroups);
+ TermFirstPassGroupingCollector c1 = new TermFirstPassGroupingCollector("author", groupSort, groupOffset+topNGroups);
boolean cacheScores = true;
double maxCacheRAMMB = 4.0;
CachingCollector cachedCollector = CachingCollector.create(c1, cacheScores, maxCacheRAMMB);
s.search(new TermQuery(new Term("content", searchTerm)), cachedCollector);
- Collection topGroups = c1.getTopGroups(groupOffset, fillFields);
+ Collection> topGroups = c1.getTopGroups(groupOffset, fillFields);
if (topGroups == null) {
// No groups matched
@@ -118,12 +100,12 @@ But the downside is it's somewhat slower to run, and requires more RAM
boolean getScores = true;
boolean getMaxScores = true;
boolean fillFields = true;
- SecondPassGroupingCollector c2 = new SecondPassGroupingCollector("author", topGroups, groupSort, docSort, docOffset+docsPerGroup, getScores, getMaxScores, fillFields);
+ TermSecondPassGroupingCollector c2 = new TermSecondPassGroupingCollector("author", topGroups, groupSort, docSort, docOffset+docsPerGroup, getScores, getMaxScores, fillFields);
//Optionally compute total group count
- AllGroupsCollector allGroupsCollector = null;
+ TermAllGroupsCollector allGroupsCollector = null;
if (requiredTotalGroupCount) {
- allGroupsCollector = new AllGroupsCollector("author");
+ allGroupsCollector = new TermAllGroupsCollector("author");
c2 = MultiCollector.wrap(c2, allGroupsCollector);
}
@@ -135,9 +117,9 @@ But the downside is it's somewhat slower to run, and requires more RAM
s.search(new TermQuery(new Term("content", searchTerm)), c2);
}
- TopGroups groupsResult = c2.getTopGroups(docOffset);
+ TopGroups groupsResult = c2.getTopGroups(docOffset);
if (requiredTotalGroupCount) {
- groupResult = new TopGroups(groupsResult, allGroupsCollector.getGroupCount());
+ groupResult = new TopGroups(groupsResult, allGroupsCollector.getGroupCount());
}
// Render groupsResult...
diff --git a/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupsCollectorTest.java b/modules/grouping/src/test/org/apache/lucene/search/grouping/TermAllGroupsCollectorTest.java
similarity index 93%
rename from modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupsCollectorTest.java
rename to modules/grouping/src/test/org/apache/lucene/search/grouping/TermAllGroupsCollectorTest.java
index 00153e7f997..0e6004e0696 100644
--- a/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupsCollectorTest.java
+++ b/modules/grouping/src/test/org/apache/lucene/search/grouping/TermAllGroupsCollectorTest.java
@@ -27,7 +27,7 @@ import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.LuceneTestCase;
-public class AllGroupsCollectorTest extends LuceneTestCase {
+public class TermAllGroupsCollectorTest extends LuceneTestCase {
public void testTotalGroupCount() throws Exception {
@@ -91,15 +91,15 @@ public class AllGroupsCollectorTest extends LuceneTestCase {
IndexSearcher indexSearcher = new IndexSearcher(w.getReader());
w.close();
- AllGroupsCollector c1 = new AllGroupsCollector(groupField);
+ TermAllGroupsCollector c1 = new TermAllGroupsCollector(groupField);
indexSearcher.search(new TermQuery(new Term("content", "random")), c1);
assertEquals(4, c1.getGroupCount());
- AllGroupsCollector c2 = new AllGroupsCollector(groupField);
+ TermAllGroupsCollector c2 = new TermAllGroupsCollector(groupField);
indexSearcher.search(new TermQuery(new Term("content", "some")), c2);
assertEquals(3, c2.getGroupCount());
- AllGroupsCollector c3 = new AllGroupsCollector(groupField);
+ TermAllGroupsCollector c3 = new TermAllGroupsCollector(groupField);
indexSearcher.search(new TermQuery(new Term("content", "blob")), c3);
assertEquals(2, c3.getGroupCount());
diff --git a/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java b/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
index 9dc49faa71b..89a9ecb9329 100644
--- a/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
+++ b/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
@@ -17,9 +17,6 @@
package org.apache.lucene.search.grouping;
-import java.util.*;
-import java.io.IOException;
-
import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
@@ -33,6 +30,9 @@ import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util._TestUtil;
+import java.io.IOException;
+import java.util.*;
+
// TODO
// - should test relevance sort too
// - test null
@@ -103,10 +103,10 @@ public class TestGrouping extends LuceneTestCase {
w.close();
final Sort groupSort = Sort.RELEVANCE;
- final FirstPassGroupingCollector c1 = new FirstPassGroupingCollector(groupField, groupSort, 10);
+ final TermFirstPassGroupingCollector c1 = new TermFirstPassGroupingCollector(groupField, groupSort, 10);
indexSearcher.search(new TermQuery(new Term("content", "random")), c1);
- final SecondPassGroupingCollector c2 = new SecondPassGroupingCollector(groupField, c1.getTopGroups(0, true), groupSort, null, 5, true, false, true);
+ final TermSecondPassGroupingCollector c2 = new TermSecondPassGroupingCollector(groupField, c1.getTopGroups(0, true), groupSort, null, 5, true, false, true);
indexSearcher.search(new TermQuery(new Term("content", "random")), c2);
final TopGroups groups = c2.getTopGroups(0);
@@ -154,7 +154,10 @@ public class TestGrouping extends LuceneTestCase {
final BytesRef group;
final BytesRef sort1;
final BytesRef sort2;
+ // content must be "realN ..."
final String content;
+ float score;
+ float score2;
public GroupDoc(int id, BytesRef group, BytesRef sort1, BytesRef sort2, String content) {
this.id = id;
@@ -167,16 +170,21 @@ public class TestGrouping extends LuceneTestCase {
private Sort getRandomSort() {
final List sortFields = new ArrayList();
- if (random.nextBoolean()) {
+ if (random.nextInt(7) == 2) {
+ sortFields.add(SortField.FIELD_SCORE);
+ } else {
if (random.nextBoolean()) {
+ if (random.nextBoolean()) {
+ sortFields.add(new SortField("sort1", SortField.STRING, random.nextBoolean()));
+ } else {
+ sortFields.add(new SortField("sort2", SortField.STRING, random.nextBoolean()));
+ }
+ } else if (random.nextBoolean()) {
sortFields.add(new SortField("sort1", SortField.STRING, random.nextBoolean()));
- } else {
sortFields.add(new SortField("sort2", SortField.STRING, random.nextBoolean()));
}
- } else if (random.nextBoolean()) {
- sortFields.add(new SortField("sort1", SortField.STRING, random.nextBoolean()));
- sortFields.add(new SortField("sort2", SortField.STRING, random.nextBoolean()));
}
+ // Break ties:
sortFields.add(new SortField("id", SortField.INT));
return new Sort(sortFields.toArray(new SortField[sortFields.size()]));
}
@@ -188,7 +196,15 @@ public class TestGrouping extends LuceneTestCase {
public int compare(GroupDoc d1, GroupDoc d2) {
for(SortField sf : sortFields) {
final int cmp;
- if (sf.getField().equals("sort1")) {
+ if (sf.getType() == SortField.SCORE) {
+ if (d1.score > d2.score) {
+ cmp = -1;
+ } else if (d1.score < d2.score) {
+ cmp = 1;
+ } else {
+ cmp = 0;
+ }
+ } else if (sf.getField().equals("sort1")) {
cmp = d1.sort1.compareTo(d2.sort1);
} else if (sf.getField().equals("sort2")) {
cmp = d1.sort2.compareTo(d2.sort2);
@@ -213,7 +229,9 @@ public class TestGrouping extends LuceneTestCase {
for(int fieldIDX=0;fieldIDX c;
final SortField sf = sortFields[fieldIDX];
- if (sf.getField().equals("sort1")) {
+ if (sf.getType() == SortField.SCORE) {
+ c = new Float(d.score);
+ } else if (sf.getField().equals("sort1")) {
c = d.sort1;
} else if (sf.getField().equals("sort2")) {
c = d.sort2;
@@ -236,18 +254,18 @@ public class TestGrouping extends LuceneTestCase {
}
*/
- private TopGroups slowGrouping(GroupDoc[] groupDocs,
- String searchTerm,
- boolean fillFields,
- boolean getScores,
- boolean getMaxScores,
- boolean doAllGroups,
- Sort groupSort,
- Sort docSort,
- int topNGroups,
- int docsPerGroup,
- int groupOffset,
- int docOffset) {
+ private TopGroups slowGrouping(GroupDoc[] groupDocs,
+ String searchTerm,
+ boolean fillFields,
+ boolean getScores,
+ boolean getMaxScores,
+ boolean doAllGroups,
+ Sort groupSort,
+ Sort docSort,
+ int topNGroups,
+ int docsPerGroup,
+ int groupOffset,
+ int docOffset) {
final Comparator groupSortComp = getComparator(groupSort);
@@ -262,11 +280,11 @@ public class TestGrouping extends LuceneTestCase {
//System.out.println("TEST: slowGrouping");
for(GroupDoc d : groupDocs) {
// TODO: would be better to filter by searchTerm before sorting!
- if (!d.content.equals(searchTerm)) {
+ if (!d.content.startsWith(searchTerm)) {
continue;
}
totalHitCount++;
- //System.out.println(" match id=" + d.id);
+ //System.out.println(" match id=" + d.id + " score=" + d.score);
if (doAllGroups) {
if (!knownGroups.contains(d.group)) {
@@ -296,7 +314,8 @@ public class TestGrouping extends LuceneTestCase {
final int limit = Math.min(groupOffset + topNGroups, groups.size());
final Comparator docSortComp = getComparator(docSort);
- final GroupDocs[] result = new GroupDocs[limit-groupOffset];
+ @SuppressWarnings("unchecked")
+ final GroupDocs[] result = new GroupDocs[limit-groupOffset];
int totalGroupedHitCount = 0;
for(int idx=groupOffset;idx < limit;idx++) {
final BytesRef group = sortedGroups.get(idx);
@@ -311,9 +330,9 @@ public class TestGrouping extends LuceneTestCase {
final GroupDoc d = docs.get(docIDX);
final FieldDoc fd;
if (fillFields) {
- fd = new FieldDoc(d.id, 0.0f, fillFields(d, docSort));
+ fd = new FieldDoc(d.id, getScores ? d.score : Float.NaN, fillFields(d, docSort));
} else {
- fd = new FieldDoc(d.id, 0.0f);
+ fd = new FieldDoc(d.id, getScores ? d.score : Float.NaN);
}
hits[docIDX-docOffset] = fd;
}
@@ -321,7 +340,7 @@ public class TestGrouping extends LuceneTestCase {
hits = new ScoreDoc[0];
}
- result[idx-groupOffset] = new GroupDocs(0.0f,
+ result[idx-groupOffset] = new GroupDocs(0.0f,
docs.size(),
hits,
group,
@@ -329,12 +348,12 @@ public class TestGrouping extends LuceneTestCase {
}
if (doAllGroups) {
- return new TopGroups(
- new TopGroups(groupSort.getSort(), docSort.getSort(), totalHitCount, totalGroupedHitCount, result),
+ return new TopGroups(
+ new TopGroups(groupSort.getSort(), docSort.getSort(), totalHitCount, totalGroupedHitCount, result),
knownGroups.size()
);
} else {
- return new TopGroups(groupSort.getSort(), docSort.getSort(), totalHitCount, totalGroupedHitCount, result);
+ return new TopGroups(groupSort.getSort(), docSort.getSort(), totalHitCount, totalGroupedHitCount, result);
}
}
@@ -372,7 +391,7 @@ public class TestGrouping extends LuceneTestCase {
doc.add(newField("sort1", groupValue.sort1.utf8ToString(), Field.Index.NOT_ANALYZED));
doc.add(newField("sort2", groupValue.sort2.utf8ToString(), Field.Index.NOT_ANALYZED));
doc.add(new NumericField("id").setIntValue(groupValue.id));
- doc.add(newField("content", groupValue.content, Field.Index.NOT_ANALYZED));
+ doc.add(newField("content", groupValue.content, Field.Index.ANALYZED));
//System.out.println("TEST: doc content=" + groupValue.content + " group=" + (groupValue.group == null ? "null" : groupValue.group.utf8ToString()) + " sort1=" + groupValue.sort1.utf8ToString() + " id=" + groupValue.id);
}
// So we can pull filter marking last doc in block:
@@ -420,7 +439,22 @@ public class TestGrouping extends LuceneTestCase {
groups.add(new BytesRef(_TestUtil.randomRealisticUnicodeString(random)));
//groups.add(new BytesRef(_TestUtil.randomSimpleString(random)));
}
- final String[] contentStrings = new String[] {"a", "b", "c", "d"};
+ final String[] contentStrings = new String[_TestUtil.nextInt(random, 2, 20)];
+ if (VERBOSE) {
+ System.out.println("TEST: create fake content");
+ }
+ for(int contentIDX=0;contentIDX> scoreMap = new HashMap>();
+
+ // Tricky: must separately set .score2, because the doc
+ // block index was created with possible deletions!
+ //System.out.println("fixup score2");
+ for(int contentID=0;contentID<3;contentID++) {
+ //System.out.println(" term=real" + contentID);
+ final Map termScoreMap = new HashMap();
+ scoreMap.put("real"+contentID, termScoreMap);
+ //System.out.println("term=real" + contentID + " dfold=" + s.docFreq(new Term("content", "real"+contentID)) +
+ //" dfnew=" + s2.docFreq(new Term("content", "real"+contentID)));
+ final ScoreDoc[] hits = s2.search(new TermQuery(new Term("content", "real"+contentID)), numDocs).scoreDocs;
+ for(ScoreDoc hit : hits) {
+ final GroupDoc gd = groupDocsByID[docIDToID2[hit.doc]];
+ assertTrue(gd.score2 == 0.0);
+ gd.score2 = hit.score;
+ assertEquals(gd.id, docIDToID2[hit.doc]);
+ //System.out.println(" score=" + gd.score + " score2=" + hit.score + " id=" + docIDToID2[hit.doc]);
+ termScoreMap.put(gd.score, gd.score2);
+ }
+ }
+
for(int searchIter=0;searchIter<100;searchIter++) {
if (VERBOSE) {
System.out.println("TEST: searchIter=" + searchIter);
}
- final String searchTerm = contentStrings[random.nextInt(contentStrings.length)];
+ final String searchTerm = "real" + random.nextInt(3);
final boolean fillFields = random.nextBoolean();
- final boolean getScores = random.nextBoolean();
+ boolean getScores = random.nextBoolean();
final boolean getMaxScores = random.nextBoolean();
final Sort groupSort = getRandomSort();
//final Sort groupSort = new Sort(new SortField[] {new SortField("sort1", SortField.STRING), new SortField("id", SortField.INT)});
// TODO: also test null (= sort by relevance)
final Sort docSort = getRandomSort();
+ for(SortField sf : docSort.getSort()) {
+ if (sf.getType() == SortField.SCORE) {
+ getScores = true;
+ }
+ }
+
+ for(SortField sf : groupSort.getSort()) {
+ if (sf.getType() == SortField.SCORE) {
+ getScores = true;
+ }
+ }
+
final int topNGroups = _TestUtil.nextInt(random, 1, 30);
//final int topNGroups = 4;
final int docsPerGroup = _TestUtil.nextInt(random, 1, 50);
+
final int groupOffset = _TestUtil.nextInt(random, 0, (topNGroups-1)/2);
//final int groupOffset = 0;
@@ -522,17 +616,17 @@ public class TestGrouping extends LuceneTestCase {
final boolean doCache = random.nextBoolean();
final boolean doAllGroups = random.nextBoolean();
if (VERBOSE) {
- System.out.println("TEST: groupSort=" + groupSort + " docSort=" + docSort + " searchTerm=" + searchTerm + " topNGroups=" + topNGroups + " groupOffset=" + groupOffset + " docOffset=" + docOffset + " doCache=" + doCache + " docsPerGroup=" + docsPerGroup + " doAllGroups=" + doAllGroups);
+ System.out.println("TEST: groupSort=" + groupSort + " docSort=" + docSort + " searchTerm=" + searchTerm + " topNGroups=" + topNGroups + " groupOffset=" + groupOffset + " docOffset=" + docOffset + " doCache=" + doCache + " docsPerGroup=" + docsPerGroup + " doAllGroups=" + doAllGroups + " getScores=" + getScores + " getMaxScores=" + getMaxScores);
}
- final AllGroupsCollector allGroupsCollector;
+ final TermAllGroupsCollector allGroupsCollector;
if (doAllGroups) {
- allGroupsCollector = new AllGroupsCollector("group");
+ allGroupsCollector = new TermAllGroupsCollector("group");
} else {
allGroupsCollector = null;
}
- final FirstPassGroupingCollector c1 = new FirstPassGroupingCollector("group", groupSort, groupOffset+topNGroups);
+ final TermFirstPassGroupingCollector c1 = new TermFirstPassGroupingCollector("group", groupSort, groupOffset+topNGroups);
final CachingCollector cCache;
final Collector c;
@@ -583,19 +677,19 @@ public class TestGrouping extends LuceneTestCase {
}
}
- final Collection topGroups = c1.getTopGroups(groupOffset, fillFields);
+ final Collection> topGroups = c1.getTopGroups(groupOffset, fillFields);
final TopGroups groupsResult;
if (topGroups != null) {
if (VERBOSE) {
System.out.println("TEST: topGroups");
- for (SearchGroup searchGroup : topGroups) {
+ for (SearchGroup searchGroup : topGroups) {
System.out.println(" " + (searchGroup.groupValue == null ? "null" : searchGroup.groupValue.utf8ToString()) + ": " + Arrays.deepToString(searchGroup.sortValues));
}
}
- final SecondPassGroupingCollector c2 = new SecondPassGroupingCollector("group", topGroups, groupSort, docSort, docOffset+docsPerGroup, getScores, getMaxScores, fillFields);
+ final TermSecondPassGroupingCollector c2 = new TermSecondPassGroupingCollector("group", topGroups, groupSort, docSort, docOffset+docsPerGroup, getScores, getMaxScores, fillFields);
if (doCache) {
if (cCache.isCached()) {
if (VERBOSE) {
@@ -613,8 +707,8 @@ public class TestGrouping extends LuceneTestCase {
}
if (doAllGroups) {
- TopGroups tempTopGroups = c2.getTopGroups(docOffset);
- groupsResult = new TopGroups(tempTopGroups, allGroupsCollector.getGroupCount());
+ TopGroups tempTopGroups = c2.getTopGroups(docOffset);
+ groupsResult = new TopGroups(tempTopGroups, allGroupsCollector.getGroupCount());
} else {
groupsResult = c2.getTopGroups(docOffset);
}
@@ -625,49 +719,93 @@ public class TestGrouping extends LuceneTestCase {
}
}
- final TopGroups expectedGroups = slowGrouping(groupDocs, searchTerm, fillFields, getScores, getMaxScores, doAllGroups, groupSort, docSort, topNGroups, docsPerGroup, groupOffset, docOffset);
+ final TopGroups expectedGroups = slowGrouping(groupDocs, searchTerm, fillFields, getScores, getMaxScores, doAllGroups, groupSort, docSort, topNGroups, docsPerGroup, groupOffset, docOffset);
if (VERBOSE) {
if (expectedGroups == null) {
System.out.println("TEST: no expected groups");
} else {
System.out.println("TEST: expected groups");
- for(GroupDocs gd : expectedGroups.groups) {
+ for(GroupDocs gd : expectedGroups.groups) {
System.out.println(" group=" + (gd.groupValue == null ? "null" : gd.groupValue.utf8ToString()));
for(ScoreDoc sd : gd.scoreDocs) {
- System.out.println(" id=" + sd.doc);
+ System.out.println(" id=" + sd.doc + " score=" + sd.score);
}
}
}
}
- // NOTE: intentional but temporary field cache insanity!
- assertEquals(docIDToID, expectedGroups, groupsResult, true);
+ assertEquals(docIDToID, expectedGroups, groupsResult, true, getScores);
final boolean needsScores = getScores || getMaxScores || docSort == null;
final BlockGroupingCollector c3 = new BlockGroupingCollector(groupSort, groupOffset+topNGroups, needsScores, lastDocInBlock);
- final AllGroupsCollector allGroupsCollector2;
+ final TermAllGroupsCollector allGroupsCollector2;
final Collector c4;
if (doAllGroups) {
- allGroupsCollector2 = new AllGroupsCollector("group");
+ allGroupsCollector2 = new TermAllGroupsCollector("group");
c4 = MultiCollector.wrap(c3, allGroupsCollector2);
} else {
allGroupsCollector2 = null;
c4 = c3;
}
s2.search(new TermQuery(new Term("content", searchTerm)), c4);
- final TopGroups tempTopGroups2 = c3.getTopGroups(docSort, groupOffset, docOffset, docOffset+docsPerGroup, fillFields);
+ @SuppressWarnings("unchecked")
+ final TopGroups tempTopGroups2 = c3.getTopGroups(docSort, groupOffset, docOffset, docOffset+docsPerGroup, fillFields);
final TopGroups groupsResult2;
if (doAllGroups && tempTopGroups2 != null) {
assertEquals((int) tempTopGroups2.totalGroupCount, allGroupsCollector2.getGroupCount());
- groupsResult2 = new TopGroups(tempTopGroups2, allGroupsCollector2.getGroupCount());
+ groupsResult2 = new TopGroups(tempTopGroups2, allGroupsCollector2.getGroupCount());
} else {
groupsResult2 = tempTopGroups2;
}
- assertEquals(docIDToID2, expectedGroups, groupsResult2, false);
+
+ if (expectedGroups != null) {
+ // Fixup scores for reader2
+ for (GroupDocs groupDocsHits : expectedGroups.groups) {
+ for(ScoreDoc hit : groupDocsHits.scoreDocs) {
+ final GroupDoc gd = groupDocsByID[hit.doc];
+ assertEquals(gd.id, hit.doc);
+ //System.out.println("fixup score " + hit.score + " to " + gd.score2 + " vs " + gd.score);
+ hit.score = gd.score2;
+ }
+ }
+
+ final SortField[] sortFields = groupSort.getSort();
+ final Map termScoreMap = scoreMap.get(searchTerm);
+ for(int groupSortIDX=0;groupSortIDX sources = fp.parseValueSourceList();
+ return new MultiBoolFunction(sources) {
+ @Override
+ protected String name() {
+ return "and";
+ }
+ @Override
+ protected boolean func(int doc, DocValues[] vals) {
+ for (DocValues dv : vals)
+ if (!dv.boolVal(doc)) return false;
+ return true;
+ }
+ };
+ }
+ });
+
+ addParser("or", new ValueSourceParser() {
+ @Override
+ public ValueSource parse(FunctionQParser fp) throws ParseException {
+ List sources = fp.parseValueSourceList();
+ return new MultiBoolFunction(sources) {
+ @Override
+ protected String name() {
+ return "or";
+ }
+ @Override
+ protected boolean func(int doc, DocValues[] vals) {
+ for (DocValues dv : vals)
+ if (dv.boolVal(doc)) return true;
+ return false;
+ }
+ };
+ }
+ });
+
+ addParser("xor", new ValueSourceParser() {
+ @Override
+ public ValueSource parse(FunctionQParser fp) throws ParseException {
+ List sources = fp.parseValueSourceList();
+ return new MultiBoolFunction(sources) {
+ @Override
+ protected String name() {
+ return "xor";
+ }
+ @Override
+ protected boolean func(int doc, DocValues[] vals) {
+ int nTrue=0, nFalse=0;
+ for (DocValues dv : vals) {
+ if (dv.boolVal(doc)) nTrue++;
+ else nFalse++;
+ }
+ return nTrue != 0 && nFalse != 0;
+ }
+ };
+ }
+ });
+
+ addParser("if", new ValueSourceParser() {
+ @Override
+ public ValueSource parse(FunctionQParser fp) throws ParseException {
+ ValueSource ifValueSource = fp.parseValueSource();
+ ValueSource trueValueSource = fp.parseValueSource();
+ ValueSource falseValueSource = fp.parseValueSource();
+
+ return new IfFunction(ifValueSource, trueValueSource, falseValueSource);
+ }
+ });
+
+ addParser("def", new ValueSourceParser() {
+ @Override
+ public ValueSource parse(FunctionQParser fp) throws ParseException {
+ return new DefFunction(fp.parseValueSourceList());
+ }
+ });
+
}
private static TInfo parseTerm(FunctionQParser fp) throws ParseException {
@@ -857,6 +985,11 @@ class LongConstValueSource extends ConstNumberSource {
public Number getNumber() {
return constant;
}
+
+ @Override
+ public boolean getBool() {
+ return constant != 0;
+ }
}
@@ -981,3 +1114,69 @@ abstract class Double2Parser extends NamedParser {
}
}
+
+
+class BoolConstValueSource extends ConstNumberSource {
+ final boolean constant;
+
+ public BoolConstValueSource(boolean constant) {
+ this.constant = constant;
+ }
+
+ @Override
+ public String description() {
+ return "const(" + constant + ")";
+ }
+
+ @Override
+ public DocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
+ return new BoolDocValues(this) {
+ @Override
+ public boolean boolVal(int doc) {
+ return constant;
+ }
+ };
+ }
+
+ @Override
+ public int hashCode() {
+ return constant ? 0x12345678 : 0x87654321;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (BoolConstValueSource.class != o.getClass()) return false;
+ BoolConstValueSource other = (BoolConstValueSource) o;
+ return this.constant == other.constant;
+ }
+
+ @Override
+ public int getInt() {
+ return constant ? 1 : 0;
+ }
+
+ @Override
+ public long getLong() {
+ return constant ? 1 : 0;
+ }
+
+ @Override
+ public float getFloat() {
+ return constant ? 1 : 0;
+ }
+
+ @Override
+ public double getDouble() {
+ return constant ? 1 : 0;
+ }
+
+ @Override
+ public Number getNumber() {
+ return constant ? 1 : 0;
+ }
+
+ @Override
+ public boolean getBool() {
+ return constant;
+ }
+}
diff --git a/solr/src/java/org/apache/solr/search/function/BoolDocValues.java b/solr/src/java/org/apache/solr/search/function/BoolDocValues.java
new file mode 100644
index 00000000000..443f379ab95
--- /dev/null
+++ b/solr/src/java/org/apache/solr/search/function/BoolDocValues.java
@@ -0,0 +1,79 @@
+package org.apache.solr.search.function;
+
+import org.apache.solr.search.MutableValue;
+import org.apache.solr.search.MutableValueBool;
+import org.apache.solr.search.MutableValueInt;
+
+public abstract class BoolDocValues extends DocValues {
+ protected final ValueSource vs;
+
+ public BoolDocValues(ValueSource vs) {
+ this.vs = vs;
+ }
+
+ @Override
+ public abstract boolean boolVal(int doc);
+
+ @Override
+ public byte byteVal(int doc) {
+ return boolVal(doc) ? (byte)1 : (byte)0;
+ }
+
+ @Override
+ public short shortVal(int doc) {
+ return boolVal(doc) ? (short)1 : (short)0;
+ }
+
+ @Override
+ public float floatVal(int doc) {
+ return boolVal(doc) ? (float)1 : (float)0;
+ }
+
+ @Override
+ public int intVal(int doc) {
+ return boolVal(doc) ? 1 : 0;
+ }
+
+ @Override
+ public long longVal(int doc) {
+ return boolVal(doc) ? (long)1 : (long)0;
+ }
+
+ @Override
+ public double doubleVal(int doc) {
+ return boolVal(doc) ? (double)1 : (double)0;
+ }
+
+ @Override
+ public String strVal(int doc) {
+ return Boolean.toString(boolVal(doc));
+ }
+
+ @Override
+ public Object objectVal(int doc) {
+ return exists(doc) ? boolVal(doc) : null;
+ }
+
+ @Override
+ public String toString(int doc) {
+ return vs.description() + '=' + strVal(doc);
+ }
+
+ @Override
+ public ValueFiller getValueFiller() {
+ return new ValueFiller() {
+ private final MutableValueBool mval = new MutableValueBool();
+
+ @Override
+ public MutableValue getValue() {
+ return mval;
+ }
+
+ @Override
+ public void fillValue(int doc) {
+ mval.value = boolVal(doc);
+ mval.exists = exists(doc);
+ }
+ };
+ }
+}
diff --git a/solr/src/java/org/apache/solr/search/function/BoolFunction.java b/solr/src/java/org/apache/solr/search/function/BoolFunction.java
new file mode 100644
index 00000000000..b7898d15184
--- /dev/null
+++ b/solr/src/java/org/apache/solr/search/function/BoolFunction.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search.function;
+
+
+public abstract class BoolFunction extends ValueSource {
+ // TODO: placeholder to return type, among other common future functionality
+}
diff --git a/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java b/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java
index da9ef1f051a..fac0611cc71 100755
--- a/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java
+++ b/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java
@@ -26,4 +26,5 @@ public abstract class ConstNumberSource extends ValueSource {
public abstract float getFloat();
public abstract double getDouble();
public abstract Number getNumber();
+ public abstract boolean getBool();
}
diff --git a/solr/src/java/org/apache/solr/search/function/ConstValueSource.java b/solr/src/java/org/apache/solr/search/function/ConstValueSource.java
index ad495a18007..fc0b9334427 100755
--- a/solr/src/java/org/apache/solr/search/function/ConstValueSource.java
+++ b/solr/src/java/org/apache/solr/search/function/ConstValueSource.java
@@ -66,6 +66,10 @@ public class ConstValueSource extends ConstNumberSource {
public Object objectVal(int doc) {
return constant;
}
+ @Override
+ public boolean boolVal(int doc) {
+ return constant != 0.0f;
+ }
};
}
@@ -105,4 +109,9 @@ public class ConstValueSource extends ConstNumberSource {
public Number getNumber() {
return constant;
}
+
+ @Override
+ public boolean getBool() {
+ return constant != 0.0f;
+ }
}
diff --git a/solr/src/java/org/apache/solr/search/function/DefFunction.java b/solr/src/java/org/apache/solr/search/function/DefFunction.java
new file mode 100644
index 00000000000..b2f99a3c6fa
--- /dev/null
+++ b/solr/src/java/org/apache/solr/search/function/DefFunction.java
@@ -0,0 +1,124 @@
+package org.apache.solr.search.function;
+/**
+ * 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.
+ */
+
+import org.apache.lucene.index.IndexReader.AtomicReaderContext;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.util.BytesRef;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class DefFunction extends MultiFunction {
+ public DefFunction(List sources) {
+ super(sources);
+ }
+
+ @Override
+ protected String name() {
+ return "def";
+ }
+
+
+ @Override
+ public DocValues getValues(Map fcontext, AtomicReaderContext readerContext) throws IOException {
+
+
+ return new Values(valsArr(sources, fcontext, readerContext)) {
+ final int upto = valsArr.length - 1;
+
+ private DocValues get(int doc) {
+ for (int i=0; i {
final double[] arr = vals.values;
final Bits valid = vals.valid;
- return new DocValues() {
- @Override
- public float floatVal(int doc) {
- return (float) arr[doc];
- }
-
- @Override
- public int intVal(int doc) {
- return (int) arr[doc];
- }
-
- @Override
- public long longVal(int doc) {
- return (long) arr[doc];
- }
-
+ return new DoubleDocValues(this) {
@Override
public double doubleVal(int doc) {
return arr[doc];
}
@Override
- public String strVal(int doc) {
- return Double.toString(arr[doc]);
- }
-
- @Override
- public Object objectVal(int doc) {
- return valid.get(doc) ? arr[doc] : null;
- }
-
- @Override
- public String toString(int doc) {
- return description() + '=' + doubleVal(doc);
+ public boolean exists(int doc) {
+ return valid.get(doc);
}
@Override
@@ -147,7 +122,7 @@ public class DoubleFieldSource extends NumericFieldCacheSource {
}
}
- @Override
+ @Override
public ValueFiller getValueFiller() {
return new ValueFiller() {
private final double[] doubleArr = arr;
diff --git a/solr/src/java/org/apache/solr/search/function/IfFunction.java b/solr/src/java/org/apache/solr/search/function/IfFunction.java
new file mode 100644
index 00000000000..00ad2f437d6
--- /dev/null
+++ b/solr/src/java/org/apache/solr/search/function/IfFunction.java
@@ -0,0 +1,148 @@
+/**
+ * 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.search.function;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexReader.AtomicReaderContext;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.util.BytesRef;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+
+public class IfFunction extends BoolFunction {
+ private ValueSource ifSource;
+ private ValueSource trueSource;
+ private ValueSource falseSource;
+
+
+ public IfFunction(ValueSource ifSource, ValueSource trueSource, ValueSource falseSource) {
+ this.ifSource = ifSource;
+ this.trueSource = trueSource;
+ this.falseSource = falseSource;
+ }
+
+ @Override
+ public DocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
+ final DocValues ifVals = ifSource.getValues(context, readerContext);
+ final DocValues trueVals = trueSource.getValues(context, readerContext);
+ final DocValues falseVals = falseSource.getValues(context, readerContext);
+
+ return new DocValues() {
+ @Override
+ public byte byteVal(int doc) {
+ return ifVals.boolVal(doc) ? trueVals.byteVal(doc) : falseVals.byteVal(doc);
+ }
+
+ @Override
+ public short shortVal(int doc) {
+ return ifVals.boolVal(doc) ? trueVals.shortVal(doc) : falseVals.shortVal(doc);
+ }
+
+ @Override
+ public float floatVal(int doc) {
+ return ifVals.boolVal(doc) ? trueVals.floatVal(doc) : falseVals.floatVal(doc);
+ }
+
+ @Override
+ public int intVal(int doc) {
+ return ifVals.boolVal(doc) ? trueVals.intVal(doc) : falseVals.intVal(doc);
+ }
+
+ @Override
+ public long longVal(int doc) {
+ return ifVals.boolVal(doc) ? trueVals.longVal(doc) : falseVals.longVal(doc);
+ }
+
+ @Override
+ public double doubleVal(int doc) {
+ return ifVals.boolVal(doc) ? trueVals.doubleVal(doc) : falseVals.doubleVal(doc);
+ }
+
+ @Override
+ public String strVal(int doc) {
+ return ifVals.boolVal(doc) ? trueVals.strVal(doc) : falseVals.strVal(doc);
+ }
+
+ @Override
+ public boolean boolVal(int doc) {
+ return ifVals.boolVal(doc) ? trueVals.boolVal(doc) : falseVals.boolVal(doc);
+ }
+
+ @Override
+ public boolean bytesVal(int doc, BytesRef target) {
+ return ifVals.boolVal(doc) ? trueVals.bytesVal(doc, target) : falseVals.bytesVal(doc, target);
+ }
+
+ @Override
+ public Object objectVal(int doc) {
+ return ifVals.boolVal(doc) ? trueVals.objectVal(doc) : falseVals.objectVal(doc);
+ }
+
+ @Override
+ public boolean exists(int doc) {
+ return true; // TODO: flow through to any sub-sources?
+ }
+
+ @Override
+ public ValueFiller getValueFiller() {
+ // TODO: we need types of trueSource / falseSource to handle this
+ // for now, use float.
+ return super.getValueFiller();
+ }
+
+ @Override
+ public String toString(int doc) {
+ return "if(" + ifVals.toString(doc) + ',' + trueVals.toString(doc) + ',' + falseVals.toString(doc) + ')';
+ }
+ };
+
+ }
+
+ @Override
+ public String description() {
+ return "if(" + ifSource.description() + ',' + trueSource.description() + ',' + falseSource + ')';
+ }
+
+ @Override
+ public int hashCode() {
+ int h = ifSource.hashCode();
+ h = h * 31 + trueSource.hashCode();
+ h = h * 31 + falseSource.hashCode();
+ return h;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof IfFunction)) return false;
+ IfFunction other = (IfFunction)o;
+ return ifSource.equals(other.ifSource)
+ && trueSource.equals(other.trueSource)
+ && falseSource.equals(other.falseSource);
+ }
+
+ @Override
+ public void createWeight(Map context, IndexSearcher searcher) throws IOException {
+ ifSource.createWeight(context, searcher);
+ trueSource.createWeight(context, searcher);
+ falseSource.createWeight(context, searcher);
+ }
+}
\ No newline at end of file
diff --git a/solr/src/java/org/apache/solr/search/function/LongDocValues.java b/solr/src/java/org/apache/solr/search/function/LongDocValues.java
index f5117bd0d43..f0e8f6d8ee9 100644
--- a/solr/src/java/org/apache/solr/search/function/LongDocValues.java
+++ b/solr/src/java/org/apache/solr/search/function/LongDocValues.java
@@ -38,6 +38,11 @@ public abstract class LongDocValues extends DocValues {
return (double)longVal(doc);
}
+ @Override
+ public boolean boolVal(int doc) {
+ return longVal(doc) != 0;
+ }
+
@Override
public String strVal(int doc) {
return Long.toString(longVal(doc));
diff --git a/solr/src/java/org/apache/solr/search/function/MultiBoolFunction.java b/solr/src/java/org/apache/solr/search/function/MultiBoolFunction.java
new file mode 100644
index 00000000000..033ef6ebae9
--- /dev/null
+++ b/solr/src/java/org/apache/solr/search/function/MultiBoolFunction.java
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search.function;
+
+import org.apache.lucene.index.IndexReader.AtomicReaderContext;
+import org.apache.lucene.search.IndexSearcher;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+
+public abstract class MultiBoolFunction extends BoolFunction {
+ protected final List sources;
+
+ public MultiBoolFunction(List sources) {
+ this.sources = sources;
+ }
+
+ protected abstract String name();
+
+ protected abstract boolean func(int doc, DocValues[] vals);
+
+ @Override
+ public BoolDocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
+ final DocValues[] vals = new DocValues[sources.size()];
+ int i=0;
+ for (ValueSource source : sources) {
+ vals[i++] = source.getValues(context, readerContext);
+ }
+
+ return new BoolDocValues(this) {
+ @Override
+ public boolean boolVal(int doc) {
+ return func(doc, vals);
+ }
+
+ @Override
+ public String toString(int doc) {
+ StringBuilder sb = new StringBuilder(name());
+ sb.append('(');
+ boolean first = true;
+ for (DocValues dv : vals) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(dv.toString(doc));
+ }
+ return sb.toString();
+ }
+ };
+ }
+
+ @Override
+ public String description() {
+ StringBuilder sb = new StringBuilder(name());
+ sb.append('(');
+ boolean first = true;
+ for (ValueSource source : sources) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(source.description());
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return sources.hashCode() + name().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this.getClass() != o.getClass()) return false;
+ MultiBoolFunction other = (MultiBoolFunction)o;
+ return this.sources.equals(other.sources);
+ }
+
+ @Override
+ public void createWeight(Map context, IndexSearcher searcher) throws IOException {
+ for (ValueSource source : sources) {
+ source.createWeight(context, searcher);
+ }
+ }
+}
\ No newline at end of file
diff --git a/solr/src/java/org/apache/solr/search/function/MultiFunction.java b/solr/src/java/org/apache/solr/search/function/MultiFunction.java
new file mode 100644
index 00000000000..941b3415ba7
--- /dev/null
+++ b/solr/src/java/org/apache/solr/search/function/MultiFunction.java
@@ -0,0 +1,122 @@
+package org.apache.solr.search.function;
+/**
+ * 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.
+ */
+
+import org.apache.lucene.index.IndexReader.AtomicReaderContext;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.util.BytesRef;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+
+public abstract class MultiFunction extends ValueSource {
+ protected final List sources;
+
+ public MultiFunction(List sources) {
+ this.sources = sources;
+ }
+
+ abstract protected String name();
+
+ @Override
+ public String description() {
+ return description(name(), sources);
+ }
+
+ public static String description(String name, List sources) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(name).append('(');
+ boolean firstTime=true;
+ for (ValueSource source : sources) {
+ if (firstTime) {
+ firstTime=false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(source);
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
+ public static DocValues[] valsArr(List sources, Map fcontext, AtomicReaderContext readerContext) throws IOException {
+ final DocValues[] valsArr = new DocValues[sources.size()];
+ int i=0;
+ for (ValueSource source : sources) {
+ valsArr[i++] = source.getValues(fcontext, readerContext);
+ }
+ return valsArr;
+ }
+
+ public class Values extends DocValues {
+ final DocValues[] valsArr;
+
+ public Values(DocValues[] valsArr) {
+ this.valsArr = valsArr;
+ }
+
+ @Override
+ public String toString(int doc) {
+ return MultiFunction.toString(name(), valsArr, doc);
+ }
+
+ @Override
+ public ValueFiller getValueFiller() {
+ // TODO: need ValueSource.type() to determine correct type
+ return super.getValueFiller();
+ }
+ }
+
+
+ public static String toString(String name, DocValues[] valsArr, int doc) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(name).append('(');
+ boolean firstTime=true;
+ for (DocValues vals : valsArr) {
+ if (firstTime) {
+ firstTime=false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(vals.toString(doc));
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
+ @Override
+ public void createWeight(Map context, IndexSearcher searcher) throws IOException {
+ for (ValueSource source : sources)
+ source.createWeight(context, searcher);
+ }
+
+ @Override
+ public int hashCode() {
+ return sources.hashCode() + name().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this.getClass() != o.getClass()) return false;
+ MultiFunction other = (MultiFunction)o;
+ return this.sources.equals(other.sources);
+ }
+}
+
diff --git a/solr/src/java/org/apache/solr/search/function/SimpleBoolFunction.java b/solr/src/java/org/apache/solr/search/function/SimpleBoolFunction.java
new file mode 100644
index 00000000000..6a4da8b229d
--- /dev/null
+++ b/solr/src/java/org/apache/solr/search/function/SimpleBoolFunction.java
@@ -0,0 +1,74 @@
+/**
+ * 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.search.function;
+
+import org.apache.lucene.index.IndexReader.AtomicReaderContext;
+import org.apache.lucene.search.IndexSearcher;
+
+import java.io.IOException;
+import java.util.Map;
+
+
+public abstract class SimpleBoolFunction extends BoolFunction {
+ protected final ValueSource source;
+
+ public SimpleBoolFunction(ValueSource source) {
+ this.source = source;
+ }
+
+ protected abstract String name();
+
+ protected abstract boolean func(int doc, DocValues vals);
+
+ @Override
+ public BoolDocValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
+ final DocValues vals = source.getValues(context, readerContext);
+ return new BoolDocValues(this) {
+ @Override
+ public boolean boolVal(int doc) {
+ return func(doc, vals);
+ }
+ @Override
+ public String toString(int doc) {
+ return name() + '(' + vals.toString(doc) + ')';
+ }
+ };
+ }
+
+ @Override
+ public String description() {
+ return name() + '(' + source.description() + ')';
+ }
+
+ @Override
+ public int hashCode() {
+ return source.hashCode() + name().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this.getClass() != o.getClass()) return false;
+ SingleFunction other = (SingleFunction)o;
+ return this.source.equals(other.source);
+ }
+
+ @Override
+ public void createWeight(Map context, IndexSearcher searcher) throws IOException {
+ source.createWeight(context, searcher);
+ }
+}
\ No newline at end of file
diff --git a/solr/src/java/org/apache/solr/search/function/StrDocValues.java b/solr/src/java/org/apache/solr/search/function/StrDocValues.java
index e4c28da47eb..5726824388c 100644
--- a/solr/src/java/org/apache/solr/search/function/StrDocValues.java
+++ b/solr/src/java/org/apache/solr/search/function/StrDocValues.java
@@ -21,6 +21,11 @@ public abstract class StrDocValues extends DocValues {
return exists(doc) ? strVal(doc) : null;
}
+ @Override
+ public boolean boolVal(int doc) {
+ return exists(doc);
+ }
+
@Override
public String toString(int doc) {
return vs.description() + "='" + strVal(doc) + "'";
diff --git a/solr/src/java/org/apache/solr/search/function/StringIndexDocValues.java b/solr/src/java/org/apache/solr/search/function/StringIndexDocValues.java
index 95d7d0cd823..71db0ab36d0 100755
--- a/solr/src/java/org/apache/solr/search/function/StringIndexDocValues.java
+++ b/solr/src/java/org/apache/solr/search/function/StringIndexDocValues.java
@@ -78,6 +78,10 @@ public abstract class StringIndexDocValues extends DocValues {
return spareChars.toString();
}
+ @Override
+ public boolean boolVal(int doc) {
+ return exists(doc);
+ }
@Override
public abstract Object objectVal(int doc); // force subclasses to override
diff --git a/solr/src/test/org/apache/solr/search/TestQueryTypes.java b/solr/src/test/org/apache/solr/search/TestQueryTypes.java
index ca49dd72b3f..efd6c68f547 100755
--- a/solr/src/test/org/apache/solr/search/TestQueryTypes.java
+++ b/solr/src/test/org/apache/solr/search/TestQueryTypes.java
@@ -119,7 +119,29 @@ public class TestQueryTypes extends AbstractSolrTestCase {
assertQ(req( "q", "{!frange v="+f+" l='"+v+"' u='"+v+"'}" )
,"//result[@numFound='1']"
);
-
+
+ // exists()
+ assertQ(req( "fq","id:999", "q", "{!frange l=1 u=1}if(exists("+f+"),1,0)" )
+ ,"//result[@numFound='1']"
+ );
+
+ // boolean value of non-zero values (just leave off the exists from the prev test)
+ assertQ(req( "fq","id:999", "q", "{!frange l=1 u=1}if("+f+",1,0)" )
+ ,"//result[@numFound='1']"
+ );
+
+ if (!"id".equals(f)) {
+ assertQ(req( "fq","id:1", "q", "{!frange l=1 u=1}if(exists("+f+"),1,0)" )
+ ,"//result[@numFound='0']"
+ );
+
+ // boolean value of zero/missing values (just leave off the exists from the prev test)
+ assertQ(req( "fq","id:1", "q", "{!frange l=1 u=1}if("+f+",1,0)" )
+ ,"//result[@numFound='0']"
+ );
+
+ }
+
// function query... just make sure it doesn't throw an exception
if ("v_s".equals(f)) continue; // in this context, functions must be able to be interpreted as a float
assertQ(req( "q", "+id:999 _val_:\"" + f + "\"")
diff --git a/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java b/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java
index 1bf6dd8edfc..4648b424126 100755
--- a/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java
+++ b/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java
@@ -581,4 +581,56 @@ public class TestFunctionQuery extends SolrTestCaseJ4 {
purgeFieldCache(FieldCache.DEFAULT); // avoid FC insanity
}
+ @Test
+ public void testBooleanFunctions() throws Exception {
+ assertU(adoc("id", "1", "text", "hello", "foo_s","A", "foo_ti", "0", "foo_tl","0"));
+ assertU(adoc("id", "2" , "foo_ti","10", "foo_tl","11"));
+ assertU(commit());
+
+ // true and false functions and constants
+ assertJQ(req("q", "id:1", "fl", "t:true(),f:false(),tt:{!func}true,ff:{!func}false")
+ , "/response/docs/[0]=={'t':true,'f':false,'tt':true,'ff':false}");
+
+ // test that exists(query) depends on the query matching the document
+ assertJQ(req("q", "id:1", "fl", "t:exists(query($q1)),f:exists(query($q2))", "q1","text:hello", "q2","text:there")
+ , "/response/docs/[0]=={'t':true,'f':false}");
+
+ // test if()
+ assertJQ(req("q", "id:1", "fl", "a1:if(true,'A','B')", "fl","b1:if(false,'A','B')")
+ , "/response/docs/[0]=={'a1':'A', 'b1':'B'}");
+
+ // test boolean operators
+ assertJQ(req("q", "id:1", "fl", "t1:and(true,true)", "fl","f1:and(true,false)", "fl","f2:and(false,true)", "fl","f3:and(false,false)")
+ , "/response/docs/[0]=={'t1':true, 'f1':false, 'f2':false, 'f3':false}");
+ assertJQ(req("q", "id:1", "fl", "t1:or(true,true)", "fl","t2:or(true,false)", "fl","t3:or(false,true)", "fl","f1:or(false,false)")
+ , "/response/docs/[0]=={'t1':true, 't2':true, 't3':true, 'f1':false}");
+ assertJQ(req("q", "id:1", "fl", "f1:xor(true,true)", "fl","t1:xor(true,false)", "fl","t2:xor(false,true)", "fl","f2:xor(false,false)")
+ , "/response/docs/[0]=={'t1':true, 't2':true, 'f1':false, 'f2':false}");
+ assertJQ(req("q", "id:1", "fl", "t:not(false),f:not(true)")
+ , "/response/docs/[0]=={'t':true, 'f':false}");
+
+
+ // def(), the default function that returns the first value that exists
+ assertJQ(req("q", "id:1", "fl", "x:def(id,123.0), y:def(foo_f,234.0)")
+ , "/response/docs/[0]=={'x':1.0, 'y':234.0}");
+ assertJQ(req("q", "id:1", "fl", "x:def(foo_s,'Q'), y:def(missing_s,'W')")
+ , "/response/docs/[0]=={'x':'A', 'y':'W'}");
+
+ // test constant conversion to boolean
+ assertJQ(req("q", "id:1", "fl", "a:not(0), b:not(1), c:not(0.0), d:not(1.1), e:not('A')")
+ , "/response/docs/[0]=={'a':true, 'b':false, 'c':true, 'd':false, 'e':false}");
+
+ }
+
+
+ @Test
+ public void testPseudoFieldFunctions() throws Exception {
+ assertU(adoc("id", "1", "text", "hello", "foo_s","A"));
+ assertU(adoc("id", "2"));
+ assertU(commit());
+
+ assertJQ(req("q", "id:1", "fl", "a:1,b:2.0,c:'X',d:{!func}foo_s,e:{!func}bar_s") // if exists() is false, no pseudo-field should be added
+ , "/response/docs/[0]=={'a':1, 'b':2.0,'c':'X','d':'A'}");
+ }
+
}
diff --git a/solr/src/webapp/web/css/screen.css b/solr/src/webapp/web/css/screen.css
index fc50d0309de..53e0387f8b1 100644
--- a/solr/src/webapp/web/css/screen.css
+++ b/solr/src/webapp/web/css/screen.css
@@ -462,6 +462,7 @@ ul
#content #dashboard .block
{
+ background-image: none;
width: 49%;
}
@@ -550,85 +551,13 @@ ul
display: block;
}
-#content #dashboard #replication.is-master .slave
+#content #dashboard #replication #details table thead td span
{
display: none;
}
-#content #dashboard #replication table
-{
- border-collapse: collapse;
-}
-
-#content #dashboard #replication table th,
-#content #dashboard #replication table td
-{
- border: 1px solid #f0f0f0;
- padding: 2px 5px;
-}
-
-#content #dashboard #replication table thead td
-{
- border: 0;
-}
-
-#content #dashboard #replication table thead th,
-#content #dashboard #replication table tbody td
-{
- border-right: 0;
-}
-
-#content #dashboard #replication table thead th
-{
- border-top: 0;
- font-weight: bold;
-}
-
-#content #dashboard #replication table tbody th,
-#content #dashboard #replication table tbody td
-{
- border-bottom: 0;
- text-align: right;
-}
-
-#content #dashboard #replication table tbody th
-{
- border-left: 0;
-}
-
-#content #dashboard #replication table tbody th,
-#content #dashboard #replication dt
-{
- width: 100px;
-}
-
-#content #dashboard #replication dl
-{
- display: none;
- margin-top: 10px;
-}
-
-#content #dashboard #replication dt,
-#content #dashboard #replication dd
-{
- display: block;
- padding-top: 1px;
- padding-bottom: 1px;
-}
-
-#content #dashboard #replication dt
-{
- border-right: 1px solid #f0f0f0;
- float: left;
- padding-left: 5px;
- padding-right: 5px;
- margin-right: 3px;
- text-align: right;
-}
-
#content #dashboard #dataimport
{
- background-color: #0ff;
float: right;
}
@@ -711,6 +640,19 @@ ul
max-width: 99%;
}
+#content #analysis #analysis-error
+{
+ background-color: #f00;
+ background-image: url( ../img/ico/construction.png );
+ background-position: 10px 50%;
+ color: #fff;
+ display: none;
+ font-weight: bold;
+ margin-bottom: 20px;
+ padding: 10px;
+ padding-left: 35px;
+}
+
#content #analysis .analysis-result h2
{
position: relative;
@@ -1334,6 +1276,12 @@ ul
padding-left: 10px;
}
+#content #schema-browser #related #f-df-t
+{
+ border-bottom: 1px solid #f0f0f0;
+ padding-bottom: 15px;
+}
+
#content #schema-browser #related dl
{
margin-top: 15px;
@@ -1367,7 +1315,9 @@ ul
#content #schema-browser #related .dynamic-field .dynamic-field,
#content #schema-browser #related .dynamic-field .dynamic-field a,
#content #schema-browser #related .type .type,
-#content #schema-browser #related .type .type a
+#content #schema-browser #related .type .type a,
+#content #schema-browser #related .active,
+#content #schema-browser #related .active a
{
color: #333;
}
@@ -1378,6 +1328,11 @@ ul
color: #666;
}
+#content #schema-browser #data
+{
+ display: none;
+}
+
#content #schema-browser #data #index dt
{
display: none;
@@ -1491,6 +1446,7 @@ ul
#content #schema-browser #data #field .topterms-holder
{
+ display: none;
float: left;
}
@@ -2830,6 +2786,7 @@ ul
#content #replication #details table tbody .size
{
text-align: right;
+ white-space: nowrap;
}
#content #replication #details table tbody .generation div
diff --git a/solr/src/webapp/web/index.jsp b/solr/src/webapp/web/index.jsp
index dec2ddc4b34..a632b365327 100644
--- a/solr/src/webapp/web/index.jsp
+++ b/solr/src/webapp/web/index.jsp
@@ -35,14 +35,14 @@
-
+
-
+
diff --git a/solr/src/webapp/web/js/script.js b/solr/src/webapp/web/js/script.js
index 264d88435a7..47f0a056e77 100644
--- a/solr/src/webapp/web/js/script.js
+++ b/solr/src/webapp/web/js/script.js
@@ -258,7 +258,7 @@ var sammy = $.sammy
// #/cores
this.get
(
- /^#\/cores$/,
+ /^#\/(cores)$/,
function( context )
{
sammy.trigger
@@ -286,7 +286,7 @@ var sammy = $.sammy
// #/cores
this.get
(
- /^#\/cores\//,
+ /^#\/(cores)\//,
function( context )
{
var content_element = $( '#content' );
@@ -386,25 +386,24 @@ var sammy = $.sammy
);
var core_names = [];
- var core_selects = $( '.swap select', cores_element );
+ var core_selects = $( '#actions select', cores_element );
for( var key in cores )
{
- core_names.push( '
' + key + ' ' )
+ core_names.push( '
' + key + ' ' )
}
-
core_selects
.html( core_names.join( "\n") );
- $( 'option[value=' + current_core + ']', core_selects.filter( '.core' ) )
+ $( 'option[value="' + current_core + '"]', core_selects.filter( '#swap_core' ) )
.attr( 'selected', 'selected' );
- $( 'option[value=' + current_core + ']', core_selects.filter( '.other' ) )
+ $( 'option[value="' + current_core + '"]', core_selects.filter( '.other' ) )
.attr( 'disabled', 'disabled' )
.addClass( 'disabled' );
- $( '.rename input[name=core]', cores_element )
+ $( 'input[name="core"]', cores_element )
.val( current_core );
// layout
@@ -445,6 +444,57 @@ var sammy = $.sammy
}
);
+ $( 'form a.submit', button_holder_element )
+ .die( 'click' )
+ .live
+ (
+ 'click',
+ function( event )
+ {
+ var element = $( this );
+ var form_element = element.parents( 'form' );
+ var action = $( 'input[name="action"]', form_element ).val().toLowerCase();
+
+ form_element
+ .ajaxSubmit
+ (
+ {
+ url : app.config.solr_path + app.config.core_admin_path + '?wt=json',
+ dataType : 'json',
+ beforeSubmit : function( array, form, options )
+ {
+ //loader
+ },
+ success : function( response, status_text, xhr, form )
+ {
+ delete app.cores_data;
+
+ if( 'rename' === action )
+ {
+ context.redirect( path_parts[1] + $( 'input[name="other"]', form_element ).val() );
+ }
+ else if( 'swap' === action )
+ {
+ window.location.reload();
+ }
+
+ $( 'a.reset', form )
+ .trigger( 'click' );
+ },
+ error : function( xhr, text_status, error_thrown )
+ {
+ },
+ complete : function()
+ {
+ //loader
+ }
+ }
+ );
+
+ return false;
+ }
+ );
+
$( 'form a.reset', button_holder_element )
.die( 'click' )
.live
@@ -452,12 +502,101 @@ var sammy = $.sammy
'click',
function( event )
{
+ $( this ).parents( 'form' )
+ .resetForm();
+
$( this ).parents( '.button-holder' )
.trigger( 'toggle' );
+
+ return false;
}
);
- $( '#actions .optimize', cores_element )
+ var reload_button = $( '#actions .reload', cores_element );
+ reload_button
+ .die( 'click' )
+ .live
+ (
+ 'click',
+ function( event )
+ {
+ $.ajax
+ (
+ {
+ url : app.config.solr_path + app.config.core_admin_path + '?wt=json&action=RELOAD&core=' + current_core,
+ dataType : 'json',
+ context : $( this ),
+ beforeSend : function( xhr, settings )
+ {
+ this
+ .addClass( 'loader' );
+ },
+ success : function( response, text_status, xhr )
+ {
+ this
+ .addClass( 'success' );
+
+ window.setTimeout
+ (
+ function()
+ {
+ reload_button
+ .removeClass( 'success' );
+ },
+ 5000
+ );
+ },
+ error : function( xhr, text_status, error_thrown )
+ {
+ },
+ complete : function( xhr, text_status )
+ {
+ this
+ .removeClass( 'loader' );
+ }
+ }
+ );
+ }
+ );
+
+ $( '#actions .unload', cores_element )
+ .die( 'click' )
+ .live
+ (
+ 'click',
+ function( event )
+ {
+ $.ajax
+ (
+ {
+ url : app.config.solr_path + app.config.core_admin_path + '?wt=json&action=UNLOAD&core=' + current_core,
+ dataType : 'json',
+ context : $( this ),
+ beforeSend : function( xhr, settings )
+ {
+ this
+ .addClass( 'loader' );
+ },
+ success : function( response, text_status, xhr )
+ {
+ delete app.cores_data;
+ context.redirect( path_parts[1].substr( 0, path_parts[1].length - 1 ) );
+ },
+ error : function( xhr, text_status, error_thrown )
+ {
+ },
+ complete : function( xhr, text_status )
+ {
+ this
+ .removeClass( 'loader' );
+ }
+ }
+ );
+ }
+ );
+
+ var optimize_button = $( '#actions .optimize', cores_element );
+ optimize_button
.die( 'click' )
.live
(
@@ -477,6 +616,19 @@ var sammy = $.sammy
},
success : function( response, text_status, xhr )
{
+ this
+ .addClass( 'success' );
+
+ window.setTimeout
+ (
+ function()
+ {
+ optimize_button
+ .removeClass( 'success' );
+ },
+ 5000
+ );
+
$( '.optimized dd.ico-0', index_data_element )
.removeClass( 'ico-0' )
.addClass( 'ico-1' );
@@ -1262,6 +1414,9 @@ var sammy = $.sammy
error : function( xhr, text_status, error_thrown )
{
console.debug( arguments );
+
+ $( '#content' )
+ .html( 'sorry, no replication-handler defined!' );
},
complete : function( xhr, text_status )
{
@@ -1323,19 +1478,44 @@ var sammy = $.sammy
'schema_browser_navi',
function( event, params )
{
- var related_navigation_element = $( '#related dl', params.schema_browser_element );
+ var related_navigation_element = $( '#related dl#f-df-t', params.schema_browser_element );
+ var related_navigation_meta = $( '#related dl.ukf-dsf', params.schema_browser_element );
var related_select_element = $( '#related select', params.schema_browser_element )
var type = 'index';
- if( !params.route_params )
+ var sammy_basepath = '#/' + $( 'p a', params.active_core ).html() + '/schema-browser';
+
+ if( !related_navigation_meta.hasClass( 'done' ) )
{
- related_navigation_element
- .hide();
-
- $( 'option:selected', related_select_element )
- .removeAttr( 'selected' );
+ if( app.schema_browser_data.unique_key_field )
+ {
+ $( '.unique-key-field', related_navigation_meta )
+ .show()
+ .after
+ (
+ '
' +
+ app.schema_browser_data.unique_key_field + ' '
+ );
+ }
+
+ if( app.schema_browser_data.default_search_field )
+ {
+ $( '.default-search-field', related_navigation_meta )
+ .show()
+ .after
+ (
+ '
' +
+ app.schema_browser_data.default_search_field + ' '
+ );
+ }
+
+ related_navigation_meta
+ .addClass( 'done' );
}
- else
+
+ if( params.route_params )
{
var type = params.route_params.splat[3];
var value = params.route_params.splat[4];
@@ -1348,7 +1528,7 @@ var sammy = $.sammy
'types' : []
}
- $( 'option[value=' + params.route_params.splat[2] + ']', related_select_element )
+ $( 'option[value="' + params.route_params.splat[2] + '"]', related_select_element )
.attr( 'selected', 'selected' );
if( 'field' === type )
@@ -1396,7 +1576,6 @@ var sammy = $.sammy
}
}
- var sammy_basepath = '#/' + $( 'p a', params.active_core ).html() + '/schema-browser';
var navigation_content = '';
if( 0 !== navigation_data.fields.length )
@@ -1464,20 +1643,41 @@ var sammy = $.sammy
.attr( 'class', type )
.html( navigation_content );
}
-
- $.get
- (
- 'tpl/schema-browser_'+ type + '.html',
- function( template )
- {
- var data_element = $( '#data', params.schema_browser_element );
+ else
+ {
+ related_navigation_element
+ .hide();
- data_element
- .html( template );
+ $( 'option:selected', related_select_element )
+ .removeAttr( 'selected' );
+ }
- params.callback( app.schema_browser_data, data_element );
- }
- );
+ if( 'field' === type && value === app.schema_browser_data.unique_key_field )
+ {
+ $( '.unique-key-field', related_navigation_meta )
+ .addClass( 'active' );
+ }
+ else
+ {
+ $( '.unique-key-field', related_navigation_meta )
+ .removeClass( 'active' );
+ }
+
+ if( 'field' === type && value === app.schema_browser_data.default_search_field )
+ {
+ $( '.default-search-field', related_navigation_meta )
+ .addClass( 'active' );
+ }
+ else
+ {
+ $( '.default-search-field', related_navigation_meta )
+ .removeClass( 'active' );
+ }
+
+ if( params.callback )
+ {
+ params.callback( app.schema_browser_data, $( '#data', params.schema_browser_element ) );
+ }
}
);
@@ -1787,22 +1987,9 @@ var sammy = $.sammy
{
var callback = function( schema_browser_data, data_element )
{
- var sammy_basepath = '#/' + $( 'p a', context.active_core ).html() + '/schema-browser'
-
- if( schema_browser_data.unique_key_field )
- {
- $( '.unique-key-field', data_element )
- .show()
- .after( '
' + schema_browser_data.unique_key_field + ' ' );
- }
-
- if( schema_browser_data.default_search_field )
- {
- $( '.default-search-field', data_element )
- .show()
- .after( '
' + schema_browser_data.default_search_field + ' ' );
- }
- }
+ data_element
+ .hide();
+ };
sammy.trigger
(
@@ -1815,20 +2002,28 @@ var sammy = $.sammy
}
);
- // #/:core/schema-browser/field/$field
+ // #/:core/schema-browser/field|dynamic-field|type/$field
this.get
(
- /^#\/([\w\d]+)\/(schema-browser)(\/(field)\/(.+))$/,
+ /^#\/([\w\d]+)\/(schema-browser)(\/(field|dynamic-field|type)\/(.+))$/,
function( context )
{
var callback = function( schema_browser_data, data_element )
{
var field = context.params.splat[4];
+
+ var type = context.params.splat[3];
+ var is_f = 'field' === type;
+ var is_df = 'dynamic-field' === type;
+ var is_t = 'type' === type;
var options_element = $( '.options', data_element );
- var sammy_basepath = '#/' + $( 'p a', context.active_core ).html() + '/schema-browser'
+ var sammy_basepath = context.path.indexOf( '/', context.path.indexOf( '/', 2 ) + 1 );
- var keystring_to_list = function( keystring )
+ data_element
+ .show();
+
+ var keystring_to_list = function( keystring, element_class )
{
var key_list = keystring.replace( /-/g, '' ).split( '' );
var list = [];
@@ -1849,7 +2044,12 @@ var sammy = $.sammy
if( option_key )
{
- list.push( '
' + option_key + ', ' );
+ list.push
+ (
+ '
' +
+ option_key +
+ ', '
+ );
}
}
@@ -1858,192 +2058,265 @@ var sammy = $.sammy
return list;
}
- // -- properties
- if( schema_browser_data.fields[field].flags )
+ var flags = null;
+
+ if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].flags )
{
- var properties_element = $( '.properties', options_element );
- var properties_keys = keystring_to_list( schema_browser_data.fields[field].flags );
+ flags = schema_browser_data.fields[field].flags;
+ }
+ else if( is_df && schema_browser_data.dynamic_fields[field] && schema_browser_data.dynamic_fields[field].flags )
+ {
+ flags = schema_browser_data.dynamic_fields[field].flags;
+ }
+
+ // -- properties
+ var properties_element = $( 'dt.properties', options_element );
+ if( flags )
+ {
+ var properties_keys = keystring_to_list( flags, 'properties' );
+
+ $( 'dd.properties', options_element )
+ .remove();
properties_element
.show()
.after( properties_keys.join( "\n" ) );
}
+ else
+ {
+ $( '.properties', options_element )
+ .hide();
+ }
// -- schema
- if( schema_browser_data.fields[field].schema )
+ var schema_element = $( 'dt.schema', options_element );
+ if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].schema )
{
- var schema_element = $( '.schema', options_element );
- var schema_keys = keystring_to_list( schema_browser_data.fields[field].schema );
+ var schema_keys = keystring_to_list( schema_browser_data.fields[field].schema, 'schema' );
+
+ $( 'dd.schema', options_element )
+ .remove();
schema_element
.show()
.after( schema_keys.join( "\n" ) );
}
+ else
+ {
+ $( '.schema', options_element )
+ .hide();
+ }
// -- index
- if( schema_browser_data.fields[field].index )
+ var index_element = $( 'dt.index', options_element );
+ if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].index )
{
- var index_element = $( '.index', options_element );
var index_keys = [];
if( 0 === schema_browser_data.fields[field].index.indexOf( '(' ) )
{
- index_keys.push( '
' + schema_browser_data.fields[field].index + ' ' );
+ index_keys.push( '
' + schema_browser_data.fields[field].index + ' ' );
}
else
{
- index_keys = keystring_to_list( schema_browser_data.fields[field].index );
+ index_keys = keystring_to_list( schema_browser_data.fields[field].index, 'index' );
}
+ $( 'dd.index', options_element )
+ .remove();
+
index_element
.show()
.after( index_keys.join( "\n" ) );
}
+ else
+ {
+ $( '.index', options_element )
+ .hide();
+ }
// -- docs
- if( schema_browser_data.fields[field].docs )
- {
- var docs_element = $( '.docs', options_element );
+ var docs_element = $( 'dt.docs', options_element );
+ if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].docs )
+ {
+ $( 'dd.docs', options_element )
+ .remove();
docs_element
.show()
- .after( '
' + schema_browser_data.fields[field].docs + ' ' );
+ .after( '
' + schema_browser_data.fields[field].docs + ' ' );
+ }
+ else
+ {
+ $( '.docs', options_element )
+ .hide();
}
// -- distinct
- if( schema_browser_data.fields[field].distinct )
+ var distinct_element = $( 'dt.distinct', options_element );
+ if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].distinct )
{
- var distinct_element = $( '.distinct', options_element );
+ $( 'dd.distinct', options_element )
+ .remove();
distinct_element
.show()
- .after( '
' + schema_browser_data.fields[field].distinct + ' ' );
+ .after( '
' + schema_browser_data.fields[field].distinct + ' ' );
+ }
+ else
+ {
+ $( '.distinct', options_element )
+ .hide();
}
// -- position-increment-gap
- if( schema_browser_data.fields[field].positionIncrementGap )
+ var pig_element = $( 'dt.position-increment-gap', options_element );
+ if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].positionIncrementGap )
{
- var pig_element = $( '.position-increment-gap', options_element );
+ $( 'dt.position-increment-gap', options_element )
+ .remove();
pig_element
.show()
- .after( '
' + schema_browser_data.fields[field].positionIncrementGap + ' ' );
+ .after( '
' + schema_browser_data.fields[field].positionIncrementGap + ' ' );
+ }
+ else
+ {
+ $( '.position-increment-gap', options_element )
+ .hide();
+ }
+
+ var analyzer_element = $( '.analyzer', data_element );
+ var analyzer_data = null;
+
+ if( is_f )
+ {
+ analyzer_data = schema_browser_data.types[schema_browser_data.relations.f_t[field]];
+ }
+ else if( is_df )
+ {
+ analyzer_data = schema_browser_data.types[schema_browser_data.relations.df_t[field]];
+ }
+ else if( is_t )
+ {
+ analyzer_data = schema_browser_data.types[field];
}
- var analyzer_data = schema_browser_data.types[schema_browser_data.relations.f_t[field]];
- var analyzer_element = $( '.analyzer', data_element );
-
- var transform_analyzer_data_into_list = function( analyzer_data )
+ if( analyzer_data )
{
- var args = [];
- for( var key in analyzer_data.args )
+ var transform_analyzer_data_into_list = function( analyzer_data )
{
- var arg_class = '';
- var arg_content = '';
+ var args = [];
+ for( var key in analyzer_data.args )
+ {
+ var arg_class = '';
+ var arg_content = '';
- if( 'true' === analyzer_data.args[key] || '1' === analyzer_data.args[key] )
- {
- arg_class = 'ico-1';
- arg_content = key;
- }
- else if( 'false' === analyzer_data.args[key] || '0' === analyzer_data.args[key] )
- {
- arg_class = 'ico-0';
- arg_content = key;
- }
- else
- {
- arg_content = key + ': ';
-
- if( 'synonyms' === key || 'words' === key )
+ if( 'true' === analyzer_data.args[key] || '1' === analyzer_data.args[key] )
{
- // @TODO: set link target for file
- arg_content += '
' + analyzer_data.args[key] + ' ';
+ arg_class = 'ico-1';
+ arg_content = key;
+ }
+ else if( 'false' === analyzer_data.args[key] || '0' === analyzer_data.args[key] )
+ {
+ arg_class = 'ico-0';
+ arg_content = key;
}
else
{
- arg_content += analyzer_data.args[key];
+ arg_content = key + ': ';
+
+ if( 'synonyms' === key || 'words' === key )
+ {
+ // @TODO: set link target for file
+ arg_content += '
' + analyzer_data.args[key] + ' ';
+ }
+ else
+ {
+ arg_content += analyzer_data.args[key];
+ }
}
+
+ args.push( '
' + arg_content + ' ' );
}
- args.push( '
' + arg_content + ' ' );
+ var list_content = '
' + analyzer_data.className + ' ';
+ if( 0 !== args.length )
+ {
+ args.sort();
+ list_content += args.join( "\n" );
+ }
+
+ return list_content;
}
- var list_content = '
' + analyzer_data.className + ' ';
- if( 0 !== args.length )
+ // -- field-type
+ var field_type_element = $( 'dt.field-type', options_element );
+
+ $( 'dd.field-type', options_element )
+ .remove();
+
+ field_type_element
+ .show()
+ .after( '
' + analyzer_data.className + ' ' );
+
+
+ for( var key in analyzer_data )
{
- args.sort();
- list_content += args.join( "\n" );
- }
-
- return list_content;
- }
-
- // -- field-type
- var field_type_element = $( '.field-type', options_element );
-
- field_type_element
- .show()
- .after( '
' + analyzer_data.className + ' ' );
-
-
- for( var key in analyzer_data )
- {
- var key_match = key.match( /^(.+)Analyzer$/ );
- if( !key_match )
- {
- continue;
- }
-
- var analyzer_key_element = $( '.' + key_match[1], analyzer_element );
- var analyzer_key_data = analyzer_data[key];
-
- analyzer_element.show();
- analyzer_key_element.show();
-
- if( analyzer_key_data.className )
- {
- $( 'dl:first dt', analyzer_key_element )
- .html( analyzer_key_data.className );
- }
-
- for( var type in analyzer_key_data )
- {
- if( 'object' !== typeof analyzer_key_data[type] )
+ var key_match = key.match( /^(.+)Analyzer$/ );
+ if( !key_match )
{
continue;
}
- var type_element = $( '.' + type, analyzer_key_element );
- var type_content = [];
+ var analyzer_key_element = $( '.' + key_match[1], analyzer_element );
+ var analyzer_key_data = analyzer_data[key];
- type_element.show();
+ analyzer_element.show();
+ analyzer_key_element.show();
- if( analyzer_key_data[type].className )
+ if( analyzer_key_data.className )
{
- type_content.push( transform_analyzer_data_into_list( analyzer_key_data[type] ) );
+ $( 'dl:first dt', analyzer_key_element )
+ .html( analyzer_key_data.className );
}
- else
+
+ $( 'ul li', analyzer_key_element )
+ .hide();
+
+ for( var type in analyzer_key_data )
{
- for( var entry in analyzer_key_data[type] )
+ if( 'object' !== typeof analyzer_key_data[type] )
{
- type_content.push( transform_analyzer_data_into_list( analyzer_key_data[type][entry] ) );
+ continue;
}
- }
- $( 'dl', type_element )
- .append( type_content.join( "\n" ) );
+ var type_element = $( '.' + type, analyzer_key_element );
+ var type_content = [];
+
+ type_element.show();
+
+ if( analyzer_key_data[type].className )
+ {
+ type_content.push( transform_analyzer_data_into_list( analyzer_key_data[type] ) );
+ }
+ else
+ {
+ for( var entry in analyzer_key_data[type] )
+ {
+ type_content.push( transform_analyzer_data_into_list( analyzer_key_data[type][entry] ) );
+ }
+ }
+
+ $( 'dl', type_element )
+ .empty()
+ .append( type_content.join( "\n" ) );
+ }
}
}
-
var topterms_holder_element = $( '.topterms-holder', data_element );
- if( !schema_browser_data.fields[field].topTerms_hash )
- {
- topterms_holder_element
- .hide();
- }
- else
+ if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].topTerms_hash )
{
topterms_holder_element
.show();
@@ -2077,6 +2350,7 @@ var sammy = $.sammy
topterms_content += '';
topterms_table_element
+ .empty()
.append( topterms_content );
$( 'tbody', topterms_table_element )
@@ -2139,14 +2413,14 @@ var sammy = $.sammy
}
);
}
-
- var histogram_holder_element = $( '.histogram-holder', data_element );
- if( !schema_browser_data.fields[field].histogram_hash )
+ else
{
- histogram_holder_element
+ topterms_holder_element
.hide();
}
- else
+
+ var histogram_holder_element = $( '.histogram-holder', data_element );
+ if( is_f && schema_browser_data.fields[field] && schema_browser_data.fields[field].histogram_hash )
{
histogram_holder_element
.show();
@@ -2184,52 +2458,11 @@ var sammy = $.sammy
}
);
}
- }
-
- sammy.trigger
- (
- 'schema_browser_load',
+ else
{
- callback : callback,
- active_core : this.active_core,
- route_params : this.params
+ histogram_holder_element
+ .hide();
}
- );
- }
- );
-
- // #/:core/schema-browser/dynamic-field/$field
- this.get
- (
- /^#\/([\w\d]+)\/(schema-browser)(\/(dynamic-field)\/(.+))$/,
- function( context )
- {
- var callback = function( schema_browser_data, data_element )
- {
- console.debug( data_element );
- }
-
- sammy.trigger
- (
- 'schema_browser_load',
- {
- callback : callback,
- active_core : this.active_core,
- route_params : this.params
- }
- );
- }
- );
-
- // #/:core/schema-browser/type/$type
- this.get
- (
- /^#\/([\w\d]+)\/(schema-browser)(\/(type)\/(.+))$/,
- function( context )
- {
- var callback = function( schema_browser_data, data_element )
- {
- console.debug( data_element );
}
sammy.trigger
@@ -2287,7 +2520,7 @@ var sammy = $.sammy
// #/:core/dataimport
this.get
(
- /^#\/([\w\d]+)\/dataimport$/,
+ /^#\/([\w\d]+)\/(dataimport)$/,
function( context )
{
sammy.trigger
@@ -2297,6 +2530,14 @@ var sammy = $.sammy
active_core : this.active_core,
callback : function( dataimport_handlers )
{
+ if( 0 === dataimport_handlers.length )
+ {
+ $( '#content' )
+ .html( 'sorry, no dataimport-handler defined!' );
+
+ return false;
+ }
+
context.redirect( context.path + '/' + dataimport_handlers[0] );
}
}
@@ -2307,7 +2548,7 @@ var sammy = $.sammy
// #/:core/dataimport
this.get
(
- /^#\/([\w\d]+)\/dataimport\//,
+ /^#\/([\w\d]+)\/(dataimport)\//,
function( context )
{
var core_basepath = this.active_core.attr( 'data-basepath' );
@@ -2341,6 +2582,7 @@ var sammy = $.sammy
active_core : context.active_core,
callback : function( dataimport_handlers )
{
+
var handlers_element = $( '.handler', form_element );
var handlers = [];
@@ -2357,7 +2599,7 @@ var sammy = $.sammy
$( 'ul', handlers_element )
.html( handlers.join( "\n") ) ;
- $( 'a[href=' + context.path + ']', handlers_element ).parent()
+ $( 'a[href="' + context.path + '"]', handlers_element ).parent()
.addClass( 'active' );
handlers_element
@@ -2529,20 +2771,11 @@ var sammy = $.sammy
{
started_at = (new Date()).toGMTString();
}
- console.debug( 'started_at @ ', started_at );
function dataimport_compute_details( response, details_element )
{
var details = [];
-
- console.debug( 'elapsed @ ', $( 'str[name="Time Elapsed"]', response ).text() );
- console.debug( 'taken @ ', $( 'str[name="Time taken "]', response ).text() );
- console.debug( 'requests @ ', $( 'str[name="Total Requests made to DataSource"]', response ).text() );
- console.debug( 'fetched @ ', $( 'str[name="Total Rows Fetched"]', response ).text() );
- console.debug( 'skipped @ ', $( 'str[name="Total Documents Skipped"]', response ).text() );
- console.debug( 'processed @ ', $( 'str[name="Total Documents Processed"]', response ).text() );
-
var requests = parseInt( $( 'str[name="Total Requests made to DataSource"]', response ).text() );
if( NaN !== requests )
{
@@ -2604,7 +2837,6 @@ var sammy = $.sammy
$( '.info strong', state_element )
.text( $( 'str[name=""]', response ).text() );
- console.debug( 'failure' );
console.debug( 'rollback @ ', rollback_element.text() );
}
else if( 'idle' === status && 0 !== messages_count )
@@ -2620,7 +2852,6 @@ var sammy = $.sammy
$( '.info strong', state_element )
.text( $( 'str[name=""]', response ).text() );
- console.debug( 'success' );
dataimport_compute_details( response, $( '.info .details', state_element ) );
}
else if( 'busy' === status )
@@ -2639,7 +2870,6 @@ var sammy = $.sammy
$( '.info strong', state_element )
.text( 'Indexing ...' );
- console.debug( 'indexing' );
dataimport_compute_details( response, $( '.info .details', state_element ) );
window.setTimeout( dataimport_fetch_status, 2000 );
@@ -2781,7 +3011,7 @@ var sammy = $.sammy
content += '
' + "\n";
content += '
' + key + ' ' + "\n";
content += '