aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/vespa/documentmodel
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /config-model/src/main/java/com/yahoo/vespa/documentmodel
Publish
Diffstat (limited to 'config-model/src/main/java/com/yahoo/vespa/documentmodel')
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentModel.java31
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentSummary.java79
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/FieldView.java61
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchDef.java126
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchField.java71
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchManager.java27
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryField.java350
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java99
8 files changed, 844 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentModel.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentModel.java
new file mode 100644
index 00000000000..6c8206d30f2
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentModel.java
@@ -0,0 +1,31 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.documentmodel;
+
+import com.yahoo.documentmodel.DocumentTypeRepo;
+
+/**
+ * DocumentModel represents everything derived from a set of search definitions.
+ * It contains a document manager managing all defined document types.
+ * It contains a search manager managing all specified search definitions.
+ * It contains a storage manager managing all specified storage definitions.
+ *
+ * @author balder
+ * @since 2010-02-19
+ */
+public class DocumentModel {
+ private DocumentTypeRepo documentMan = new DocumentTypeRepo();
+ private SearchManager searchMan = new SearchManager();
+
+ /**
+ *
+ * @return Returns the DocumentManager
+ */
+ public DocumentTypeRepo getDocumentManager() { return documentMan; }
+
+ /**
+ *
+ * @return Returns the SearchManager
+ */
+ public SearchManager getSearchManager() { return searchMan; }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentSummary.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentSummary.java
new file mode 100644
index 00000000000..42fa7b04cf6
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/DocumentSummary.java
@@ -0,0 +1,79 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.documentmodel;
+
+import com.yahoo.document.Field;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A document summary definition - a list of summary fields.
+ *
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon S Bratseth</a>
+ */
+public class DocumentSummary extends FieldView {
+
+
+ /**
+ * Will create a DocumentSummary with the given name.
+ * @param name The name to use for this summary.
+ */
+ public DocumentSummary(String name) {
+ super(name);
+ }
+
+ /**
+ * The model is constrained to ensure that summary fields of the same name
+ * in different classes have the same summary transform, because this is
+ * what is supported by the backend currently.
+ * @param summaryField The summaryfield to add
+ */
+ public void add(SummaryField summaryField) {
+ summaryField.addDestination(getName());
+ super.add(summaryField);
+ }
+
+ public SummaryField getSummaryField(String name) {
+ return (SummaryField) get(name);
+ }
+
+ public Collection<SummaryField> getSummaryFields() {
+ ArrayList<SummaryField> fields = new ArrayList<>(getFields().size());
+ for(Field f : getFields()) {
+ fields.add((SummaryField) f);
+ }
+ return fields;
+ }
+
+ /**
+ * Removes implicit fields which shouldn't be included.
+ * This is implicitly added fields which are sources for
+ * other fields. We then assume they are not intended to be added
+ * implicitly in additon.
+ * This should be called when this summary is complete.
+ */
+ public void purgeImplicits() {
+ List<SummaryField> falseImplicits = new ArrayList<>();
+ for (SummaryField summaryField : getSummaryFields() ) {
+ if (summaryField.isImplicit()) continue;
+ for (Iterator<SummaryField.Source> j = summaryField.sourceIterator(); j.hasNext(); ) {
+ String sourceName = j.next().getName();
+ if (sourceName.equals(summaryField.getName())) continue;
+ SummaryField sourceField=getSummaryField(sourceName);
+ if (sourceField==null) continue;
+ if (!sourceField.isImplicit()) continue;
+ falseImplicits.add(sourceField);
+ }
+ }
+ for (SummaryField field : falseImplicits) {
+ remove(field.getName());
+ }
+ }
+
+ public String toString() {
+ return "document summary '" + getName() + "'";
+ }
+
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/FieldView.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/FieldView.java
new file mode 100644
index 00000000000..dfb44aef917
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/FieldView.java
@@ -0,0 +1,61 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.documentmodel;
+
+import com.yahoo.document.Field;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author balder
+ * @since 2010-02-19
+ */
+public class FieldView implements Serializable {
+ private String name;
+ private Map<String, Field> fields = new LinkedHashMap<>();
+
+ /**
+ * Creates a view with a name
+ * @param name Name of the view.
+ */
+ public FieldView(String name) {
+ this.name = name;
+ }
+ public String getName() { return name; }
+ public Collection<Field> getFields() { return fields.values(); }
+ public Field get(String name) { return fields.get(name); }
+ public void remove(String name) { fields.remove(name); }
+
+ /**
+ * This method will add a field to a view. All fields must come from the same document type. Not enforced here.
+ * @param field The field to add.
+ * @return Itself for chaining purposes.
+ */
+ public FieldView add(Field field) {
+ if (fields.containsKey(field.getName())) {
+ if ( ! fields.get(field.getName()).equals(field)) {
+ throw new IllegalArgumentException(
+ "View '" + name + "' already contains a field with name '" +
+ field.getName() + "' and definition : " +
+ fields.get(field.getName()).toString() + ". Your is : " + field.toString());
+ }
+ } else {
+ fields.put(field.getName(), field);
+ }
+ return this;
+ }
+
+ /**
+ * This method will join the two views.
+ * @param view The view to be joined in to this.
+ * @return Itself for chaining.
+ */
+ public FieldView add(FieldView view) {
+ for(Field field : view.getFields()) {
+ add(field);
+ }
+ return this;
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchDef.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchDef.java
new file mode 100644
index 00000000000..07b7c973841
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchDef.java
@@ -0,0 +1,126 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.documentmodel;
+
+import com.yahoo.document.DataType;
+import com.yahoo.document.DocumentType;
+import com.yahoo.document.DocumentTypeManager;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+/**
+ * @author balder
+ * @since 2010-02-19
+ */
+public class SearchDef {
+ private final static Logger log = Logger.getLogger(SearchDef.class.getName());
+ /// Name of the searchdefinition
+ private String name;
+ /// These are the real backing documenttypes
+ private DocumentTypeManager sources = new DocumentTypeManager();
+ /// Map of all search fields
+ private Map<String, SearchField> fields = new HashMap<>();
+ /// Map of all views that can be searched.
+ private Map<String, FieldView> views = new HashMap<>();
+ /// Map of all aliases <alias, realname>
+ private Map<String, String> aliases = new HashMap<>();
+
+ /**
+ * Will create a SearchDef with the given name
+ * @param name The name of the searchdefinition
+ */
+ public SearchDef(String name) {
+ this.name = name;
+ }
+
+ /**
+ * This will provide you with the name of the searchdefinition.
+ * @return The name of the searchdefinition.
+ */
+ public String getName() { return name; }
+
+ public Map<String, SearchField> getFields() { return fields; }
+ public Map<String, FieldView> getViews() { return views; }
+
+ /**
+ * Adds a document that can be mapped to this search.
+ * @param source A document that can be mapped to this search.
+ * @return Itself for chaining.
+ */
+ public SearchDef add(DataType source) {
+ sources.register(source);
+ return this;
+ }
+
+ private void noShadowing(String name) {
+ noFieldShadowing(name);
+ noViewShadowing(name);
+ }
+
+ private void noFieldShadowing(String name) {
+ if (fields.containsKey(name)) {
+ throw new IllegalArgumentException("Searchdef '" + getName() + "' already contains the fields '" + fields.toString() +
+ "'. You are trying to add '" + name + "'. Shadowing is not supported");
+ }
+ }
+
+ private void noViewShadowing(String name) {
+ if (views.containsKey(name)) {
+ throw new IllegalArgumentException("Searchdef '" + getName() + "' already contains a view with name '" +
+ name + "'. Shadowing is not supported.");
+ }
+ }
+
+ /**
+ * Adds a search field to the definition.
+ * @param field The field to add.
+ * @return Itself for chaining.
+ */
+ public SearchDef add(SearchField field) {
+ try {
+ noFieldShadowing(field.getName());
+ fields.put(field.getName(), field);
+ } catch (IllegalArgumentException e) {
+ if (views.containsKey(field.getName())) {
+ throw e;
+ }
+ }
+ return this;
+ }
+
+ public SearchDef addAlias(String alias, String aliased) {
+ noShadowing(alias);
+ if (!fields.containsKey(aliased) && !views.containsKey(aliased)) {
+ if (aliased.contains(".")) {
+ // TODO Here we should nest ourself down to something that really exists.
+ log.warning("Aliased item '" + aliased + "' not verifiable. Allowing it to be aliased to '" + alias + " for now. Validation will come when URL/Position is structified.");
+ } else {
+ throw new IllegalArgumentException("Searchdef '" + getName() + "' has nothing named '" + aliased + "'to alias to '" + alias + "'.");
+ }
+ }
+ String oldAliased = aliases.get(alias);
+ if ((oldAliased != null)) {
+ if (oldAliased.equals(aliased)) {
+ throw new IllegalArgumentException("Searchdef '" + getName() + "' already has the alias '" + alias +
+ "' to '" + aliased + ". Why do you want to add it again.");
+
+ } else {
+ throw new IllegalArgumentException("Searchdef '" + getName() + "' already has the alias '" + alias +
+ "' to '" + oldAliased + ". Cannot change it to alias '" + aliased + "'.");
+ }
+ } else {
+ aliases.put(alias, aliased);
+ }
+ return this;
+ }
+
+ public SearchDef add(FieldView view) {
+ noViewShadowing(view.getName());
+ if (views.containsKey(view.getName())) {
+ views.get(view.getName()).add(view);
+ }
+ views.put(view.getName(), view);
+ return this;
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchField.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchField.java
new file mode 100644
index 00000000000..2db81861955
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchField.java
@@ -0,0 +1,71 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.documentmodel;
+
+import com.yahoo.document.DataType;
+import com.yahoo.document.Field;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author balder
+ * @since 2010-02-19
+ */
+public class SearchField extends Field {
+ /// Indicate if field shall be stored in memory for attribute usage.
+ private boolean attribute = false;
+ /// Indicate if the field is Vespa indexed.
+ private boolean indexed = false;
+ /// Indication to backend on how much optimization should be done.
+
+ /**
+ * This is a representation of features to generate for this field.
+ * It can be both optimize hints, and real functional hints.
+ */
+ public enum Feature {
+ WEIGHT_IN_ATTRIBUTE_POSTINGS("WeightInAttributePosting"), // Hint to put the weight in postings for attribute.
+ WORDPOS_IN_POSTINGS("WordPosInPosting"), // Default for generating posocc
+ FILTER_ONLY("FilterOnly"); // Might only generate bitvector
+ private String name;
+ Feature(String name) { this.name = name;}
+ public String getName() { return name; }
+ }
+ private List<Feature> featureList = new ArrayList<>();
+
+ public SearchField(Field field, boolean indexed, boolean attribute) {
+ this(field, indexed, attribute, null);
+ }
+ public SearchField(Field field, boolean indexed, boolean attribute, List<Feature> features) {
+ super(field.getName(), field);
+ this.attribute = attribute;
+ this.indexed = indexed;
+ if (features != null) {
+ featureList.addAll(features);
+ }
+ validate();
+ }
+
+ @SuppressWarnings({ "deprecation" })
+ private void validate() {
+ if (attribute || !indexed) {
+ return;
+ }
+ DataType fieldType = getDataType();
+ DataType primiType = fieldType.getPrimitiveType();
+ if (DataType.STRING.equals(primiType) || DataType.URI.equals(primiType)) {
+ return;
+ }
+ throw new IllegalStateException("Expected type " + DataType.STRING.getName() + " for indexed field '" +
+ getName() + "', got " + fieldType.getName() + ".");
+ }
+
+ public SearchField setIndexed() { indexed = true; validate(); return this; }
+ public SearchField setAttribute() { attribute = true; validate(); return this; }
+ public boolean isAttribute() { return attribute; }
+ /**
+ * True if field is Vespa indexed
+ * @return true if indexed
+ */
+ public boolean isIndexed() { return indexed; }
+ public SearchField addFeature(Feature feature) { featureList.add(feature); validate(); return this; }
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchManager.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchManager.java
new file mode 100644
index 00000000000..29a960f7e7b
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SearchManager.java
@@ -0,0 +1,27 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.documentmodel;
+
+import java.util.TreeMap;
+
+/**
+ * @author balder
+ * @since 2010-02-19
+ */
+public class SearchManager {
+ /// This is the list of all known search definitions
+ private TreeMap<String, SearchDef> defs = new TreeMap<>();
+
+ /**
+ * This will add a searchdefinition or throw an IllegalArgumentException if the name is already used
+ * @param def The searchdef to add
+ * @return itself for chaining purposes.
+ */
+ public SearchManager add(SearchDef def) {
+ if (defs.containsKey(def.getName())) {
+ throw new IllegalArgumentException("There already exist a searchdefinition with this content:\n" +
+ defs.get(def.getName()).toString() + "\n No room for : " + def.toString());
+ }
+ defs.put(def.getName(), def);
+ return this;
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryField.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryField.java
new file mode 100644
index 00000000000..f6db82785b0
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryField.java
@@ -0,0 +1,350 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.documentmodel;
+
+import com.yahoo.document.DataType;
+import com.yahoo.document.Field;
+import com.yahoo.searchdefinition.document.TypedKey;
+
+import java.io.Serializable;
+import java.util.*;
+
+import static com.yahoo.text.Lowercase.toLowerCase;
+
+/**
+ * A summary field
+ *
+ * @author bratseth
+ */
+public class SummaryField extends Field implements Cloneable, TypedKey {
+
+ /**
+ * This class represents a source (field name) and the type of the source (only used for smart summary)
+ */
+ public static class Source implements Serializable {
+ public static enum Type {
+ CONTEXTUAL("contextual"),
+ TITLE("title"),
+ STATIC("static"),
+ URL("url");
+ private final String name;
+ Type(String name) {
+ this.name = name;
+ }
+ public String getName() { return name; }
+ }
+ private String name;
+ private Type type;
+ private boolean override = false;
+ public Source(String name) {
+ this.name = name;
+ this.type = Type.CONTEXTUAL;
+ }
+ public Source(String name, Type type) {
+ this.name = name;
+ this.type = type;
+ }
+ public String getName() { return name; }
+ public Type getType() { return type; }
+ public void setOverride(boolean override) { this.override = override; }
+ public boolean getOverride() { return override; }
+ public int hashCode() {
+ return name.hashCode() + type.getName().hashCode() + Boolean.valueOf(override).hashCode();
+ }
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Source)) {
+ return false;
+ }
+ Source other = (Source)obj;
+ return name.equals(other.name) &&
+ type.getName().equals(other.type.getName()) &&
+ override == other.override;
+ }
+ public String toString() {
+ return name;
+ }
+ }
+
+ /** A name-value property (used for smart summary) */
+ public static class Property implements Serializable {
+ private String name;
+ private String value;
+ public Property(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+ public String getName() { return name; }
+ public String getValue() { return value; }
+ public @Override int hashCode() {
+ return name.hashCode() + 17*value.hashCode();
+ }
+ public @Override boolean equals(Object obj) {
+ if (!(obj instanceof Property)) {
+ return false;
+ }
+ Property other = (Property)obj;
+ return name.equals(other.name) && value.equals(other.value);
+ }
+ }
+
+ /** The transform to perform on the stored source */
+ private SummaryTransform transform=SummaryTransform.NONE;
+
+ /** The command used per field in vsmsummary */
+ private VsmCommand vsmCommand = VsmCommand.NONE;
+
+ /**
+ * The data sources for this output summary field, in prioritized order
+ * (use only second source if first yields no result after transformation
+ * and so on). If no sources are given explicitly, the field of the same
+ * name as this summary field is used
+ */
+ private Set<Source> sources = new java.util.LinkedHashSet<>();
+
+ private Set<String> destinations=new java.util.LinkedHashSet<>();
+
+ /** True if this field was defined implicitly */
+ private boolean implicit=false;
+
+ /** The list of properties for this summary field */
+ private List<Property> properties = new ArrayList<>();
+
+ /** Creates a summary field with NONE as transform */
+ public SummaryField(String name, DataType type) {
+ this(name,type,SummaryTransform.NONE);
+ }
+
+ /** Creates a summary field with NONE as transform */
+ public SummaryField(Field field) {
+ this(field,SummaryTransform.NONE);
+ }
+
+
+ public SummaryField(Field field,SummaryTransform transform) {
+ this(field.getName(), field.getDataType(), transform);
+ }
+
+ public SummaryField(String name,DataType type,SummaryTransform transform) {
+ super(name, type);
+ this.transform=transform;
+ }
+
+ public void setImplicit(boolean implicit) { this.implicit=implicit; }
+
+ @Override // override to make public
+ public void setDataType(DataType type) {
+ super.setDataType(type);
+ }
+
+ public boolean isImplicit() { return implicit; }
+
+ public void setTransform(SummaryTransform transform) {
+ this.transform=transform;
+ if (SummaryTransform.DYNAMICTEASER.equals(transform) || SummaryTransform.BOLDED.equals(transform)) {
+ // This is the kind of logic we want to have in processing,
+ // but can't because of deriveDocuments mode, which doesn't run
+ // processing.
+ setVsmCommand(VsmCommand.FLATTENJUNIPER);
+ }
+ }
+
+ public SummaryTransform getTransform() { return transform; }
+
+ /** Returns the first source field of this, or null if the source field is not present */
+ public String getSourceField() {
+ String sourceName=getName();
+ if (sources.size()>0)
+ sourceName=sources.iterator().next().getName();
+ return sourceName;
+ }
+
+ public void addSource(String name) {
+ sources.add(new Source(name));
+ }
+
+ public void addSource(Source source) {
+ sources.add(source);
+ }
+
+ public Iterator<Source> sourceIterator() {
+ return sources.iterator();
+ }
+
+ public int getSourceCount() {
+ return sources.size();
+ }
+
+ /** Returns a modifiable set of the sources of this */
+ public Set<Source> getSources() { return sources; }
+
+ /** Returns the first source name of this, or the field name if no source has been set */
+ public String getSingleSource() {
+ if (sources.size()==0) return getName();
+ return sources.iterator().next().getName();
+ }
+
+ public void addDestination(String name) {
+ destinations.add(name);
+ }
+
+ public final void addDestinations(Iterable<String> names) {
+ for (String name : names) {
+ addDestination(name);
+ }
+ }
+
+ /** Returns an modifiable view of the destination set owned by this */
+ public Set<String> getDestinations() {
+ return destinations;
+ }
+
+ private String toString(Collection<?> collection) {
+ StringBuffer buffer=new StringBuffer();
+ for (Iterator<?> i=collection.iterator(); i.hasNext(); ) {
+ buffer.append(i.next().toString());
+ if (i.hasNext())
+ buffer.append(", ");
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Returns a summary field which merges the settings in the given field
+ * into this field
+ *
+ * @param merge the field to merge with this, if null, the merged field is
+ * <code>this</code> field
+ * @throws RuntimeException if the two fields can not be merged
+ */
+ public SummaryField mergeWith(SummaryField merge) {
+ if (merge==null) return this;
+ if (this.isImplicit()) return merge;
+ if (merge.isImplicit()) return this;
+
+ if (!merge.getName().equals(getName()))
+ throw new IllegalArgumentException(merge + " conflicts with " + this +
+ ": different names");
+
+ if (!merge.getTransform().equals(getTransform()))
+ throw new IllegalArgumentException(merge + " conflicts with " + this +
+ ": different transforms");
+
+ if (!merge.getDataType().equals(getDataType()))
+ throw new IllegalArgumentException(merge + " conflicts with " + this +
+ ": different types");
+
+ if (!merge.isImplicit())
+ setImplicit(false);
+
+ if (isHeadOf(this.sourceIterator(),merge.sourceIterator())) {
+ // Ok
+ }
+ else if (isHeadOf(merge.sourceIterator(),this.sourceIterator())) {
+ sources=new LinkedHashSet<>(merge.sources);
+ }
+ else {
+ throw new IllegalArgumentException(merge + " conflicts with " + this +
+ ": on source list must be the start of the other");
+ }
+
+ destinations.addAll(merge.destinations);
+
+ return this;
+ }
+
+ public boolean hasSource(String name) {
+ for (Source s : sources) {
+ if (s.getName().equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the second list is the start of the first list
+ */
+ private boolean isHeadOf(Iterator<?> full, Iterator<?> head) {
+ while (head.hasNext()) {
+ if (!full.hasNext()) return false;
+
+ if (!full.next().equals(head.next())) return false;
+ }
+ return true;
+ }
+
+ private String getDestinationString()
+ {
+ StringBuilder destinationString = new StringBuilder("destinations(");
+ for (String destination : destinations) {
+ destinationString.append(destination).append(" ");
+ }
+ destinationString.append(")");
+ return destinationString.toString();
+ }
+
+ public String toString() {
+ return
+ "summary field '" + getName() + ' ' + getDestinationString() +
+ "' [type: '" + getDataType().getName() +
+ "' transform: '" + transform +
+ "', source: '" + toString(sources) +
+ "', to '" + toString(destinations) + "']";
+ }
+
+ /** returns a string which aids locating this field in the source search definition */
+ public String toLocateString() {
+ return "'summary " + getName() + " type " + toLowerCase(getDataType().getName()) + "' in '" + getDestinationString() + "'";
+ }
+
+ public SummaryField clone() {
+ try {
+ SummaryField clone=(SummaryField)super.clone();
+ if (this.sources!=null)
+ clone.sources=new LinkedHashSet<>(this.sources);
+ if (this.destinations!=null)
+ clone.destinations=new LinkedHashSet<>(destinations);
+ return clone;
+ }
+ catch (CloneNotSupportedException e) {
+ throw new RuntimeException("Programming error");
+ }
+ }
+
+ public VsmCommand getVsmCommand() {
+ return vsmCommand;
+ }
+
+ public void setVsmCommand(VsmCommand vsmCommand) {
+ this.vsmCommand = vsmCommand;
+ }
+
+ /** Adds a property to this summary field */
+ public void addProperty(String name, String value) {
+ properties.add(new Property(name, value));
+ }
+
+ public List<Property> getProperties() {
+ return properties;
+ }
+
+ /**
+ * The command used when using data from this SummaryField to generate StreamingSummary config (vsmsummary).
+ * Not used for ordinary Summary config.
+ * @author vegardh
+ *
+ */
+ public enum VsmCommand {
+ NONE("NONE"),
+ FLATTENSPACE("FLATTENSPACE"),
+ FLATTENJUNIPER("FLATTENJUNIPER");
+
+ private String cmd="";
+ private VsmCommand(String cmd) {
+ this.cmd=cmd;
+ }
+ @Override
+ public String toString() {
+ return cmd;
+ }
+ }
+}
diff --git a/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java
new file mode 100644
index 00000000000..05092d50951
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/documentmodel/SummaryTransform.java
@@ -0,0 +1,99 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.documentmodel;
+
+/**
+ * A value class representing a search time
+ * transformation on a summary field.
+ *
+ * @author bratseth
+ */
+public enum SummaryTransform {
+
+ NONE("none"),
+ ATTRIBUTE("attribute"),
+ BOLDED("bolded"),
+ DISTANCE("distance"),
+ DYNAMICBOLDED("dynamicbolded"),
+ DYNAMICTEASER("dynamicteaser"),
+ POSITIONS("positions"),
+ RANKFEATURES("rankfeatures"),
+ SUMMARYFEATURES("summaryfeatures"),
+ TEXTEXTRACTOR("textextractor"),
+ GEOPOS("geopos");
+
+ private String name;
+
+ private SummaryTransform(String name) {
+ this.name=name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /** Returns the bolded version of this transform if possible, throws if not */
+ public SummaryTransform bold() {
+ switch (this) {
+ case NONE:
+ case BOLDED:
+ return BOLDED;
+
+ case DYNAMICBOLDED:
+ case DYNAMICTEASER:
+ return DYNAMICBOLDED;
+
+ default:
+ throw new IllegalArgumentException("Can not bold a '" + this + "' field.");
+ }
+ }
+
+ /** Returns the unbolded version of this transform */
+ public SummaryTransform unbold() {
+ switch (this) {
+ case NONE:
+ case BOLDED:
+ return NONE;
+
+ case DYNAMICBOLDED:
+ return DYNAMICTEASER;
+
+ default:
+ return this;
+ }
+ }
+
+ /** Returns whether this value is bolded */
+ public boolean isBolded() {
+ return this==BOLDED || this==DYNAMICBOLDED;
+ }
+
+ /** Whether this is dynamically generated, both teasers and bolded fields are dynamic */
+ public boolean isDynamic() {
+ return this==BOLDED || this==DYNAMICBOLDED || this==DYNAMICTEASER;
+ }
+
+ /** Returns whether this is a teaser, not the complete field value */
+ public boolean isTeaser() {
+ return this==DYNAMICBOLDED || this==DYNAMICTEASER;
+ }
+
+ /** Returns whether this transform always gets its value by accessing memory only */
+ public boolean isInMemory() {
+ switch (this) {
+ case ATTRIBUTE:
+ case DISTANCE:
+ case POSITIONS:
+ case GEOPOS:
+ case RANKFEATURES:
+ case SUMMARYFEATURES:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ public String toString() {
+ return name;
+ }
+}