001package io.ebeanservice.docstore.api.mapping;
002
003import io.ebean.annotation.DocMapping;
004import io.ebean.annotation.DocStore;
005import io.ebean.text.PathProperties;
006import io.ebean.util.SplitName;
007
008import java.util.LinkedHashMap;
009import java.util.Map;
010import java.util.Stack;
011
012/**
013 * Builds the DocumentMapping for a given bean type.
014 */
015public final class DocMappingBuilder {
016
017  private final PathProperties paths;
018  private final DocStore docStore;
019  private final Stack<DocPropertyMapping> properties = new Stack<>();
020  private final Map<String, DocPropertyMapping> map = new LinkedHashMap<>();
021
022  /**
023   * Create with the document structure paths and docStore deployment annotation.
024   */
025  public DocMappingBuilder(PathProperties paths, DocStore docStore) {
026    this.paths = paths;
027    this.docStore = docStore;
028    this.properties.push(new DocPropertyMapping());
029  }
030
031  /**
032   * Return true if the property is included in the document.
033   */
034  public boolean includesProperty(String prefix, String name) {
035    return paths.includesProperty(prefix, name);
036  }
037
038  /**
039   * Return true if the path is included in the document.
040   */
041  public boolean includesPath(String prefix, String name) {
042    return paths.includesProperty(prefix, name);
043  }
044
045  /**
046   * Add the property mapping.
047   */
048  public void add(DocPropertyMapping docMapping) {
049    DocPropertyMapping currentParent = properties.peek();
050    currentParent.addChild(docMapping);
051
052    String parentName = currentParent.name();
053    String fullName = SplitName.add(parentName, docMapping.name());
054    map.put(fullName, docMapping);
055  }
056
057  /**
058   * Push the nested object or list onto the properties stack.
059   */
060  public void push(DocPropertyMapping nested) {
061    properties.push(nested);
062  }
063
064  /**
065   * Pop the nested object or list off the properties stack.
066   */
067  public void pop() {
068    properties.pop();
069  }
070
071  /**
072   * Apply any override mappings from the top level docStore annotation.
073   */
074  public void applyMapping() {
075    for (DocMapping docMapping : docStore.mapping()) {
076      applyFieldMapping(docMapping);
077    }
078  }
079
080  private void applyFieldMapping(DocMapping docMapping) {
081    DocPropertyMapping mapping = map.get(docMapping.name());
082    if (mapping == null) {
083      throw new IllegalStateException("DocMapping for [" + docMapping.name() + "] but property not included in document?");
084    }
085    mapping.apply(docMapping);
086  }
087
088  /**
089   * Collect the mapping of properties to 'raw' properties for those marked as sortable.
090   */
091  public Map<String, String> collectSortable() {
092    DocPropertyMapping peek = properties.peek();
093    SortableVisitor visitor = new SortableVisitor();
094    peek.visit(visitor);
095    return visitor.sortableMap();
096  }
097
098  /**
099   * Create the document mapping.
100   */
101  public DocumentMapping create(String queueId, String indexName, String indexType) {
102    int shards = docStore.shards();
103    int replicas = docStore.replicas();
104    DocPropertyMapping root = properties.peek();
105    return new DocumentMapping(queueId, indexName, indexType, paths, root, shards, replicas);
106  }
107
108
109  /**
110   * Find sortable properties to build the mapping to 'raw' properties.
111   */
112  private static class SortableVisitor extends DocPropertyAdapter {
113
114    private final Map<String, String> sortableMap = new LinkedHashMap<>();
115
116    @Override
117    public void visitProperty(DocPropertyMapping property) {
118      DocPropertyOptions options = property.options();
119      if (options != null && options.isSortable()) {
120        String fullPath = pathStack.peekFullPath(property.name());
121        sortableMap.put(fullPath, fullPath + ".raw");
122      }
123    }
124
125    private Map<String, String> sortableMap() {
126      return sortableMap;
127    }
128  }
129}