1   package eu.fbk.knowledgestore.server.http.jaxrs;
2   
3   import java.io.IOException;
4   import java.util.List;
5   import java.util.Set;
6   
7   import javax.annotation.Nullable;
8   
9   import com.google.common.collect.HashMultiset;
10  import com.google.common.collect.Lists;
11  import com.google.common.collect.Multiset;
12  import com.google.common.collect.Ordering;
13  import com.google.common.collect.Sets;
14  
15  import org.openrdf.model.URI;
16  import org.openrdf.model.Value;
17  
18  import eu.fbk.knowledgestore.Session;
19  import eu.fbk.knowledgestore.data.Data;
20  import eu.fbk.knowledgestore.data.Record;
21  import eu.fbk.knowledgestore.data.Stream;
22  import eu.fbk.knowledgestore.vocabulary.KS;
23  
24  public class TableQuery {
25  
26      private static final int MAX_MENTIONS = 10000;
27  
28      private final Session session;
29  
30      public TableQuery(final Session session) {
31          this.session = session;
32      }
33  
34      public void renderPropertyOccurrencesTable(final Appendable out, final URI entityURI)
35              throws Throwable {
36  
37          // Compute the # of occurrences of each property URI in entity mentions
38          final Multiset<URI> propertyURIs = HashMultiset.create();
39          for (final Record mention : getEntityMentions(entityURI, MAX_MENTIONS)) {
40              propertyURIs.addAll(mention.getProperties());
41          }
42  
43          // Render the table
44          renderMultisetTable(out, propertyURIs);
45      }
46  
47      public void renderValueOccurrencesTable(final Appendable out, final URI entityURI,
48              final URI propertyURI) throws Throwable {
49  
50          // Compute the # of occurrences of all the values of the given property in entity mentions
51          final Multiset<Value> propertyValues = HashMultiset.create();
52          for (final Record mention : getEntityMentions(entityURI, MAX_MENTIONS)) {
53              propertyValues.addAll(mention.get(propertyURI, Value.class));
54          }
55  
56          // Render the table
57          renderMultisetTable(out, propertyValues);
58      }
59  
60      public void renderMentionTable(final URI entity, final URI property, final String value,
61              final Appendable out) throws Throwable {
62  
63          // Retrieve all the mentions satisfying the property[=value] optional filter
64          final List<Record> mentions = Lists.newArrayList();
65          for (final Record mention : getEntityMentions(entity, MAX_MENTIONS)) {
66              if (property == null || !mention.isNull(property)
67                      && (value == null || mention.get(property).contains(value))) {
68                  mentions.add(mention);
69              }
70          }
71  
72          // Render the mention table, including column toggling functionality
73          renderRecordsTable(out, mentions, null, true);
74      }
75  
76      public Stream<Record> getEntityMentions(final URI entityURI, final int maxResults)
77              throws Throwable {
78  
79          // First retrieve all the URIs of the mentions denoting the entity, via SPARQL query
80          final List<URI> mentionURIs = this.session
81                  .sparql("SELECT ?mention WHERE { $$ gaf:denotedBy ?mention}", entityURI)
82                  .execTuples().transform(URI.class, true, "mention").toList();
83  
84          // Then return a stream that returns the mention records as they are fetched from the KS
85          return this.session.retrieve(KS.MENTION).limit((long) maxResults).ids(mentionURIs).exec();
86      }
87  
88      public static <T extends Appendable> T renderMultisetTable(final T out,
89              final Multiset<?> multiset) throws IOException {
90  
91          out.append("<table class=\"display datatable\">\n");
92          out.append("<thead>\n<tr><th>Values</th><th>Occurrences</th></tr>\n</thead>\n");
93          out.append("<tbody>\n");
94          for (final Object element : multiset.elementSet()) {
95              final int occurrences = multiset.count(element);
96              out.append("<tr><td>").append(RenderUtils.render(element)).append("</td><td>")
97                      .append(Integer.toString(occurrences)).append("</td></tr>\n");
98          }
99          out.append("</tbody>\n</table>\n");
100         return out;
101     }
102 
103     public static <T extends Appendable> T renderRecordsTable(final T out,
104             final Iterable<Record> records, @Nullable List<URI> propertyURIs,
105             final boolean toggleColumns) throws IOException {
106 
107         // Extract the properties to show if not explicitly supplied
108         if (propertyURIs == null) {
109             final Set<URI> uriSet = Sets.newHashSet();
110             for (final Record record : records) {
111                 uriSet.addAll(record.getProperties());
112             }
113             propertyURIs = Ordering.from(Data.getTotalComparator()).sortedCopy(propertyURIs);
114         }
115 
116         // Emit the panel for toggling displayed columns
117         out.append("<div>Toggle column: | <a class=\"toggle-vis\" data-column=\"0\">mentionURI</a> |");
118         for (int i = 0; i < propertyURIs.size(); ++i) {
119             final String qname = RenderUtils.shortenURI(propertyURIs.get(i));
120             out.append(" <a class=\"toggle-vis\" data-column=\"").append(Integer.toString(i + 1))
121                     .append("\">").append(qname).append("</a> | ");
122         }
123         out.append("</div>");
124 
125         // Emit the table
126         out.append("<table class=\"display datatable\">\n<thead>\n<tr><th>URI</th>");
127         for (final URI propertyURI : propertyURIs) {
128             out.append("<th>").append(RenderUtils.shortenURI(propertyURI)).append("</th>");
129         }
130         out.append("</tr>\n</thead>\n<tbody>\n");
131         for (final Record record : records) {
132             out.append("<tr><td>").append(RenderUtils.render(record.getID())).append("</td>");
133             for (final URI propertyURI : propertyURIs) {
134                 out.append("<td>").append(RenderUtils.render(record.get(propertyURI)))
135                         .append("</td>");
136             }
137             out.append("</tr>\n");
138         }
139         out.append("</tbody>\n</table>\n");
140         return out;
141     }
142 
143 }