commit cc21ed5c76c74aaf011ce85783d80467ba831ec9
Author: jpoubova <jpoubova@students.zcu.cz>
Date:   Wed Apr 15 18:14:20 2020 +0200

    Re #7807: Nahrávání souborů přes frontend (upload)
    - více souborů najednou lze nahrávat přes /upload (vždy XML+obrázek)
    - přidání přes /add zrušeno
    - přidáno posílání DocumentSize (rozměry obrázku)
    - do Solru se ukládají i slova (tag Word)

diff --git a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/IndexController.java b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/IndexController.java
index a8fadf9..f3f422f 100644
--- a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/IndexController.java
+++ b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/IndexController.java
@@ -28,20 +28,20 @@ public class IndexController {
 
     @PostMapping("/upload")
     public ResponseEntity<ResponseMessage> uploadFile(@RequestParam("file") List<MultipartFile> files) {
-        String message = "";
+        String message;
         try {
-            // TODO: Převést MultipartFile na normální file a pak ho dále zpracovat
-
-            message = "Successfully uploaded " + files.size() + " files";
+            int uploaded = solrService.uploadFiles(files);
+            message = "Successfully uploaded " + uploaded + " files";
             return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message));
         } catch (Exception e) {
+            e.printStackTrace();
             message = "Could not upload the files! " + e.toString();
             return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
         }
     }
 
     @GetMapping("/documents")
-    public List<DocumentResponse> uploadFile() {
+    public List<DocumentResponse> listDocuments() {
         List<DocumentResponse> response = new ArrayList<>();
 
         try {
@@ -57,23 +57,6 @@ public class IndexController {
         return response;
     }
 
-
-
-    @GetMapping("/add")
-    public String add() {
-        String response;
-
-        try {
-            solrService.addAllDocuments();
-            response = "All documents have been added.";
-        } catch (Exception e) {
-            response = "Unknown exception";
-            e.printStackTrace();
-        }
-
-        return response;
-    }
-
     @GetMapping("/delete")
     public String delete() {
         String response;
diff --git a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/SolrService.java b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/SolrService.java
index c00652e..21c0e3f 100644
--- a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/SolrService.java
+++ b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/SolrService.java
@@ -20,101 +20,233 @@ import java.util.*;
 public class SolrService {
 
     // for docker
-    public static final String DATA_DIR = "/data/";
     private static final String URL = "http://fulltextsearch-solr";
 
     // for localhost
-    //public static final String DATA_DIR = "data/";
     //private static final String URL = "http://localhost";
 
-    private static final String CORE_NAME = "documents";
+    private static final String CORE_NAME_DOCUMENTS = "documents";
+    private static final String CORE_NAME_FILES = "files";
 
     private static final String PREFIX_TEXT_LINE = "text_line_";
+    private static final String PREFIX_TEXT_WORD = "text_word_";
     private static final String SUFFIX_COORDS = "_coords";
 
     private static final String FIELD_DOC_FILENAME = "doc_filename";
     private static final String FIELD_IMG_FILENAME = "img_filename";
     private static final String FIELD_TEXT_REGION = "text_region";
     private static final String FIELD_TEXT_REGION_COORDS = "text_region" + SUFFIX_COORDS;
+    private static final String FIELD_DOC_CONTENT = "doc_content";
+    private static final String FIELD_IMG_CONTENT = "img_content";
 
-    private HttpSolrClient solrClient;
-    private List<String> fieldNames = new ArrayList<>();
+    private static final int SEARCH_QUERY_ROWS = 100;
+
+    private HttpSolrClient solrClientDocuments;
+    private HttpSolrClient solrClientFiles;
+    private List<String> fieldNamesDocumentCore = new ArrayList<>();
 
     public SolrService() {
         connect();
     }
 
     private void connect() {
-        String urlString = URL + ":8983/solr/" + CORE_NAME;
-        solrClient = new HttpSolrClient.Builder(urlString).build();
+        String urlString = URL + ":8983/solr/" + CORE_NAME_DOCUMENTS;
+        solrClientDocuments = new HttpSolrClient.Builder(urlString).build();
+
+        urlString = URL + ":8983/solr/" + CORE_NAME_FILES;
+        solrClientFiles = new HttpSolrClient.Builder(urlString).build();
+    }
+
+    public int uploadFiles(List<MultipartFile> files) throws IOException, SolrServerException, JAXBException {
+        Map<String, List<MultipartFile>> pairs = new HashMap<>();
+
+        for (MultipartFile file: files) {
+            String filename = file.getOriginalFilename();
+            if (filename == null) {
+                System.out.println("Skipping file with empty filename.");
+                continue;
+            }
+
+            String type = file.getContentType();
+            if (type == null) {
+                System.out.println("Skipping file " + filename + " (unknown type).");
+                continue;
+            }
+
+            int index = filename.lastIndexOf('.');
+            String filenameWithoutExtension = filename;
+            if (index >= 0) {
+                filenameWithoutExtension = filename.substring(0, index);
+            }
+
+            if (type.equals(MediaType.IMAGE_JPEG_VALUE) || type.equals(MediaType.IMAGE_PNG_VALUE)
+                    || type.equals(MediaType.APPLICATION_XML_VALUE)) {
+
+                List<MultipartFile> list = pairs.get(filenameWithoutExtension);
+                if (list == null) {
+                    list = new LinkedList<>();
+                    list.add(file);
+                    pairs.put(filenameWithoutExtension, list);
+                } else {
+                    list.add(file);
+                }
+
+            } else {
+                System.out.println("Not supported type of file! File " + filename + " is type " + type + ".");
+            }
+        }
+
+        return addFiles(pairs);
     }
 
-    public void addAllDocuments() throws IOException, SolrServerException, JAXBException {
-        getFieldNamesFromSolr();
-        addBasicFieldsToSolr();
+    private int addFiles(Map<String, List<MultipartFile>> pairs) throws IOException, SolrServerException, JAXBException {
+        getFieldNamesFromDocumentCore();
+        addBasicFieldsToDocumentsCore();
+
+        addAllFieldsToFilesCore();
+
+        int count = 0;
+        Set<String> keys = pairs.keySet();
+        for (String key: keys) {
+            List<MultipartFile> pair = pairs.get(key);
+            if (pair.size() != 2) continue;
+
+            MultipartFile first = pair.get(0);
+            if (first.getContentType().equals(MediaType.APPLICATION_XML_VALUE)) {
+                addToFilesCore(first, pair.get(1));
+            } else {
+                addToFilesCore(pair.get(1), first);
+            }
+
+            count += 2;
+        }
+
+        solrClientFiles.commit();
+        solrClientDocuments.commit();
+
+        return count;
+    }
+
+    private void addToFilesCore(MultipartFile xmlFile, MultipartFile imageFile) throws IOException, SolrServerException, JAXBException {
+        SolrInputDocument solrInputDocument = new SolrInputDocument();
+
+        solrInputDocument.addField(FIELD_DOC_FILENAME, xmlFile.getOriginalFilename());
+        solrInputDocument.addField(FIELD_IMG_FILENAME, imageFile.getOriginalFilename());
+        solrInputDocument.addField(FIELD_DOC_CONTENT, xmlFile.getBytes());
+        solrInputDocument.addField(FIELD_IMG_CONTENT, Base64.getEncoder().encode(imageFile.getBytes()));
+
+        solrClientFiles.add(solrInputDocument);
 
         XMLLoader xmlLoader = new XMLLoader();
+        PcGts doc = xmlLoader.loadFile(xmlFile.getInputStream(), xmlFile.getOriginalFilename());
+        addToDocumentsCore(doc, imageFile.getOriginalFilename());
+    }
+
+    private void addAllFieldsToFilesCore() throws IOException, SolrServerException {
+        List<String> fieldNamesFilesCore = new ArrayList<>();
+
+        SchemaRequest.Fields fieldRequest = new SchemaRequest.Fields();
+        SchemaResponse.FieldsResponse fieldsResponse = fieldRequest.process(solrClientFiles);
+        List<Map<String, Object>> fields = fieldsResponse.getFields();
+
+        for (Map<String, Object> field: fields) {
+            fieldNamesFilesCore.add(field.get("name").toString());
+        }
+
+        String[] filenames = new String[]{ FIELD_DOC_FILENAME, FIELD_IMG_FILENAME };
+        String[] contents = new String[]{ FIELD_DOC_CONTENT, FIELD_IMG_CONTENT };
+
+        for (String name: filenames) {
+            if (fieldNamesFilesCore.contains(name)) continue;
 
-        // temporary solution: - TODO
-        String[] files = new String[]{
-                "soap-ch_knihovna_ascher-zeitung-1866-01-06-n1_0035_bin_rot.xml",
-                "soap-ch_knihovna_ascher-zeitung-1866-01-06-n1_0040_bin_rot.xml",
-                "soap-ch_knihovna_ascher-zeitung-1866-01-06-n1_0070_bin_rot.xml",
-                "soap-ch_knihovna_ascher-zeitung-1866-02-03-n5_0155_bin_rot.xml",
-                "soap-ch_knihovna_ascher-zeitung-1866-02-03-n5_0160_bin_rot.xml",
-                "soap-ch_knihovna_ascher-zeitung-1866-02-03-n5_0190_bin_rot.xml",
-                "soap-ch_knihovna_ascher-zeitung-1866-03-10-n10_0355_bin_rot.xml",
-                "soap-ch_knihovna_ascher-zeitung-1866-03-10-n10_0360_bin_rot.xml",
-                "soap-ch_knihovna_ascher-zeitung-1866-03-10-n10_0390_bin_rot.xml",
-                "soap-ch_knihovna_ascher-zeitung-1866-07-28-n30_1085_bin_rot.xml"
-        };
-        for (String filename: files) {
-            PcGts doc = xmlLoader.loadFile(filename);
-            System.out.println("Doc " + filename + " has been loaded from resources.");
-            addDocument(doc);
+            try {
+                SchemaRequest.Update request = getAddTextFieldRequest(name);
+                request.process(solrClientFiles);
+            } catch (Exception e) {
+                System.out.println("Couldn't add field = " + name);
+                e.printStackTrace();
+            }
+        }
+
+        for (String name: contents) {
+            if (fieldNamesFilesCore.contains(name)) continue;
+
+            try {
+                Map<String, Object> fieldAttributes = new LinkedHashMap<>();
+                fieldAttributes.put("name", name);
+                fieldAttributes.put("type", "binary");
+                fieldAttributes.put("indexed", false);
+                fieldAttributes.put("stored", true);
+                fieldAttributes.put("multiValued", false);
+                SchemaRequest.Update request = new SchemaRequest.AddField(fieldAttributes);
+                request.process(solrClientFiles);
+            } catch (Exception e) {
+                System.out.println("Couldn't add field = " + name);
+                e.printStackTrace();
+            }
         }
     }
 
-    private void addBasicFieldsToSolr() {
-        addFieldToSolr(FIELD_DOC_FILENAME);
-        addFieldToSolr(FIELD_IMG_FILENAME);
-        addFieldToSolr(FIELD_TEXT_REGION);
-        addFieldToSolr(FIELD_TEXT_REGION_COORDS);
+    private void addBasicFieldsToDocumentsCore() {
+        addFieldToDocumentCore(FIELD_DOC_FILENAME);
+        addFieldToDocumentCore(FIELD_IMG_FILENAME);
+        addFieldToDocumentCore(FIELD_TEXT_REGION);
+        addFieldToDocumentCore(FIELD_TEXT_REGION_COORDS);
     }
 
-    private void getFieldNamesFromSolr() throws IOException, SolrServerException {
+    private void getFieldNamesFromDocumentCore() throws IOException, SolrServerException {
         SchemaRequest.Fields fieldRequest = new SchemaRequest.Fields();
-        SchemaResponse.FieldsResponse fieldsResponse = fieldRequest.process(solrClient);
+        SchemaResponse.FieldsResponse fieldsResponse = fieldRequest.process(solrClientDocuments);
         List<Map<String, Object>> fields = fieldsResponse.getFields();
 
         for (Map<String, Object> field: fields) {
-            fieldNames.add(field.get("name").toString());
+            fieldNamesDocumentCore.add(field.get("name").toString());
         }
     }
 
 
-    private void addFieldToSolr(String name) {
-        if (fieldNames.contains(name)) return;
+    private void addFieldToDocumentCore(String name) {
+        if (fieldNamesDocumentCore.contains(name)) return;
 
         try {
-            Map<String, Object> fieldAttributes = new LinkedHashMap<>();
-            fieldAttributes.put("name", name);
-            fieldAttributes.put("type", "text_general");
-            fieldAttributes.put("stored", true);
-            fieldAttributes.put("multiValued", false);
-            SchemaRequest.Update addFieldRequest = new SchemaRequest.AddField(fieldAttributes);
-            addFieldRequest.process(solrClient);
-
-            fieldNames.add(name);
+            SchemaRequest.Update request = getAddTextFieldRequest(name);
+            request.process(solrClientDocuments);
+            fieldNamesDocumentCore.add(name);
         } catch (Exception e) {
             System.out.println("Couldn't add field = " + name);
             e.printStackTrace();
         }
     }
 
+    private SchemaRequest.Update getAddTextFieldRequest(String name) {
+        Map<String, Object> fieldAttributes = new LinkedHashMap<>();
+        fieldAttributes.put("name", name);
+        fieldAttributes.put("type", "text_general");
+        fieldAttributes.put("stored", true);
+        fieldAttributes.put("multiValued", false);
+        return new SchemaRequest.AddField(fieldAttributes);
+    }
+
     public void deleteAll() throws IOException, SolrServerException {
-        solrClient.deleteByQuery("*:*");
-        solrClient.commit();
+        solrClientDocuments.deleteByQuery("*:*");
+        solrClientDocuments.commit();
+
+        solrClientFiles.deleteByQuery("*:*");
+        solrClientFiles.commit();
+    }
+
+    public byte[] getImageFromFilesCore(String filename) throws IOException, SolrServerException {
+        SolrQuery solrQuery = new SolrQuery();
+        solrQuery.set("q", FIELD_IMG_FILENAME + ":" + "\"" + filename + "\"");
+        SolrDocumentList results = solrClientFiles.query(solrQuery).getResults();
+        if (results.getNumFound() < 1) {
+            System.out.println("Image " + filename + " not found!");
+        } else if (results.getNumFound() > 1) {
+            System.out.println("Image " + filename + " has multiple files. Using first one...");
+        }
+
+        SolrDocument result = results.get(0);
+        return (byte[]) result.getFieldValue(FIELD_IMG_CONTENT);
     }
 
     public SearchResponse query(String query) throws IOException, SolrServerException  {
@@ -122,7 +254,9 @@ public class SolrService {
         solrQuery.set("q", FIELD_TEXT_REGION + ":" + query);
         solrQuery.setHighlight(true);
         solrQuery.addHighlightField(PREFIX_TEXT_LINE +  "*");
-        QueryResponse response = solrClient.query(solrQuery);
+        solrQuery.addHighlightField(PREFIX_TEXT_WORD +  "*");
+        solrQuery.setRows(SEARCH_QUERY_ROWS);
+        QueryResponse response = solrClientDocuments.query(solrQuery);
 
         SolrDocumentList docList = response.getResults();
         Map<String, Map<String, List<String>>> highlight = response.getHighlighting();
@@ -144,29 +278,61 @@ public class SolrService {
             String documentName = ((String) solrDocument.getFieldValue(FIELD_DOC_FILENAME)).replaceAll(".xml", "");
             textRegion.setDocumentName(documentName);
 
-            // text lines
+            // text lines and text words
             Map<String, List<String>> map = highlight.get(solrDocument.getFieldValue("id").toString());
-            Set<String> linesNames = map.keySet();
-            List<TextLine> textLines = new LinkedList<>();
+            Set<String> linesAndWordsNames = map.keySet();
+
+            Set<String> linesNames = linesAndWordsNames.stream().filter(name -> name.startsWith(PREFIX_TEXT_LINE)).collect(Collectors.toSet());
+            Set<String> wordNames = linesAndWordsNames.stream().filter(name -> name.startsWith(PREFIX_TEXT_WORD)).collect(Collectors.toSet());
+
+            Map<String, TextLine> textLines = new HashMap<>();
             for (String lineName: linesNames) {
                 TextLine textLine = new TextLine();
 
                 textLine.setLineText((String) solrDocument.getFieldValue(lineName));
-
                 String coordsValue = (String) solrDocument.getFieldValue(lineName + SUFFIX_COORDS);
                 Point[] linePoints = Coords.parsePointString(coordsValue);
                 textLine.setLineCoords(new Coords(linePoints));
 
-                textLines.add(textLine);
+                textLines.put(lineName, textLine);
             }
 
-            textRegion.setTextLines(textLines.toArray(new TextLine[0]));
+            Map<String, List<TextWord>> textWords = new HashMap<>();
+            for (String wordName: wordNames) {
+                TextWord textWord = new TextWord();
+
+                textWord.setWordText((String) solrDocument.getFieldValue(wordName));
+                String coordsValue = (String) solrDocument.getFieldValue(wordName + SUFFIX_COORDS);
+                Point[] wordPoints = Coords.parsePointString(coordsValue);
+                textWord.setWordCoords(new Coords(wordPoints));
+
+                String lineName = wordName.substring(wordName.indexOf(PREFIX_TEXT_LINE));
+
+                List<TextWord> lineWords = textWords.get(lineName);
+                if (lineWords == null) {
+                    lineWords = new LinkedList<>();
+                }
+
+                lineWords.add(textWord);
+                textWords.put(lineName, lineWords);
+            }
+
+            for (String lineName: textWords.keySet()) {
+                TextLine line = textLines.get(lineName);
+                line.setTextWords(textWords.get(lineName).toArray(new TextWord[0]));
+            }
+
+            textRegion.setTextLines(textLines.values().toArray(new TextLine[0]));
 
             // image
             String imgFilename = (String) solrDocument.getFieldValue(FIELD_IMG_FILENAME);
-            InputStream is = getClass().getClassLoader().getResourceAsStream(SolrService.DATA_DIR + imgFilename);
-            String encodedImage = Base64.getEncoder().encodeToString(is.readAllBytes());
-            textRegion.setImageCut(encodedImage); // todo - only temporary - setting imageCut to whole document image
+            byte[] imageBytes = getImageFromFilesCore(imgFilename);
+            textRegion.setImageCut(new String(imageBytes));
+
+            // image size
+            BufferedImage image = ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(imageBytes)));
+            DocumentSize documentSize = new DocumentSize(image.getHeight(), image.getWidth());
+            textRegion.setDocumentSize(documentSize);
 
             textRegions.add(textRegion);
         }
@@ -176,7 +342,7 @@ public class SolrService {
         return searchResponse;
     }
 
-    private void addDocument(PcGts document) throws IOException, SolrServerException {
+    private void addToDocumentsCore(PcGts document, String imageFilename) throws IOException, SolrServerException {
         List<TextRegion> regions = document.getPage().getTextRegions();
         if (regions == null || regions.size() == 0) return;
 
@@ -185,7 +351,7 @@ public class SolrService {
             SolrInputDocument solrInputDocument = new SolrInputDocument();
 
             solrInputDocument.addField(FIELD_DOC_FILENAME, document.getFilename());
-            solrInputDocument.addField(FIELD_IMG_FILENAME, document.getFilename().replaceAll(".xml", ".png")); // todo
+            solrInputDocument.addField(FIELD_IMG_FILENAME, imageFilename);
             solrInputDocument.addField(FIELD_TEXT_REGION, textRegion.getTextEquiv().getUnicode());
             solrInputDocument.addField(FIELD_TEXT_REGION_COORDS, textRegion.getRegionCoords().getPointsString());
 
@@ -195,42 +361,43 @@ public class SolrService {
                 TextLine textLine = textLines[j];
                 String prefix = PREFIX_TEXT_LINE + j;
 
-                addFieldToSolr(prefix);
+                addFieldToDocumentCore(prefix);
                 solrInputDocument.addField(prefix, textLine.getTextEquiv().getUnicode());
 
-                addFieldToSolr(prefix + SUFFIX_COORDS);
+                addFieldToDocumentCore(prefix + SUFFIX_COORDS);
                 solrInputDocument.addField(prefix + SUFFIX_COORDS, textLine.getLineCoords().getPointsString());
+
+                TextWord[] textWords = textLine.getTextWords();
+                if (textWords == null || textWords.length == 0) continue;
+                for (int k = 0; k < textWords.length; k++) {
+                    TextWord textWord = textWords[k];
+                    String wordPrefix = PREFIX_TEXT_WORD + k + "_" + prefix;
+
+                    addFieldToDocumentCore(wordPrefix);
+                    solrInputDocument.addField(wordPrefix, textWord.getTextEquiv().getUnicode());
+
+                    addFieldToDocumentCore(wordPrefix + SUFFIX_COORDS);
+                    solrInputDocument.addField(wordPrefix + SUFFIX_COORDS, textWord.getWordCoords().getPointsString());
+                }
             }
 
-            solrClient.add(solrInputDocument);
+            solrClientDocuments.add(solrInputDocument);
         }
-
-        solrClient.commit();
     }
 
     public String info() throws IOException, SolrServerException {
         SolrQuery q = new SolrQuery("*:*");
         q.setRows(0);  // don't actually request any data
-        return "Number of documents in core: " + solrClient.query(q).getResults().getNumFound();
+        return "Number of documents in " + CORE_NAME_DOCUMENTS + " core: " + solrClientDocuments.query(q).getResults().getNumFound() +
+                "\nNumber of documents in " + CORE_NAME_FILES + " core: " + solrClientFiles.query(q).getResults().getNumFound();
     }
 
     public List<String> listAllFiles() throws IOException, SolrServerException {
         SolrQuery solrQuery = new SolrQuery();
         solrQuery.set("q", FIELD_DOC_FILENAME + ":*");
         solrQuery.setRows(500); // todo not a magic number!
-        SolrDocumentList docList = solrClient.query(solrQuery).getResults();
-
-        Set<String> ids = getUniqueIds(docList);
-        return new ArrayList<>(ids);
+        SolrDocumentList docList = solrClientFiles.query(solrQuery).getResults();
+        return docList.stream().map(doc -> doc.getFieldValue(FIELD_DOC_FILENAME).toString()).collect(Collectors.toList());
     }
 
-    private Set<String> getUniqueIds(SolrDocumentList documents) {
-        Set<String> ids = new HashSet<>();
-        for (SolrDocument document: documents) {
-            String id = document.getFieldValue(FIELD_DOC_FILENAME).toString();
-            ids.add(id);
-        }
-
-        return ids;
-    }
 }
diff --git a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/XMLLoader.java b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/XMLLoader.java
index 66d66b2..4e43efc 100644
--- a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/XMLLoader.java
+++ b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/XMLLoader.java
@@ -10,9 +10,7 @@ import java.util.logging.Level;
 
 public class XMLLoader {
 
-    public PcGts loadFile(String filename) throws JAXBException {
-        InputStream file = getClass().getClassLoader().getResourceAsStream(SolrService.DATA_DIR + filename);
-
+    public PcGts loadFile(InputStream file, String filename) throws JAXBException {
         JAXBContext jaxbContext = JAXBContext.newInstance(PcGts.class);
         Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
 
diff --git a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/DocumentSize.java b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/DocumentSize.java
new file mode 100644
index 0000000..00ae640
--- /dev/null
+++ b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/DocumentSize.java
@@ -0,0 +1,20 @@
+package cz.zcu.kiv.aswi.fulltextsearch.document;
+
+public class DocumentSize {
+
+    private int height;
+    private int width;
+
+    public DocumentSize(int height, int width) {
+        this.height = height;
+        this.width = width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+}
diff --git a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/TextRegion.java b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/TextRegion.java
index 5f86b3d..cc2d3cc 100644
--- a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/TextRegion.java
+++ b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/TextRegion.java
@@ -14,6 +14,7 @@ public class TextRegion {
     private String regionText;
     private TextLine[] textLines;
     private String documentName;
+    private DocumentSize documentSize;
 
     @JsonIgnore
     private TextEquiv textEquiv;
@@ -68,4 +69,12 @@ public class TextRegion {
     public void setRegionText(String regionText) {
         this.regionText = regionText;
     }
+
+    public DocumentSize getDocumentSize() {
+        return documentSize;
+    }
+
+    public void setDocumentSize(DocumentSize documentSize) {
+        this.documentSize = documentSize;
+    }
 }
diff --git a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/TextWord.java b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/TextWord.java
index 04d7846..200e844 100644
--- a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/TextWord.java
+++ b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/document/TextWord.java
@@ -1,5 +1,7 @@
 package cz.zcu.kiv.aswi.fulltextsearch.document;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -8,6 +10,9 @@ import javax.xml.bind.annotation.XmlElement;
 public class TextWord {
 
     private Coords wordCoords;
+    private String wordText;
+
+    @JsonIgnore
     private TextEquiv textEquiv;
 
     public Coords getWordCoords() {
@@ -28,4 +33,11 @@ public class TextWord {
         this.textEquiv = textEquiv;
     }
 
+    public String getWordText() {
+        return wordText;
+    }
+
+    public void setWordText(String wordText) {
+        this.wordText = wordText;
+    }
 }
diff --git a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/model/QueryResponse.java b/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/model/QueryResponse.java
deleted file mode 100644
index 98f80c9..0000000
--- a/be/fulltextsearch/src/main/java/cz/zcu/kiv/aswi/fulltextsearch/model/QueryResponse.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package cz.zcu.kiv.aswi.fulltextsearch.model;
-
-public class QueryResponse {
-
-    private String response;
-
-    public QueryResponse(String response) {
-        this.response = response;
-    }
-
-    public String getResponse() {
-        return response;
-    }
-
-    public void setResponse(String response) {
-        this.response = response;
-    }
-}
diff --git a/be/fulltextsearch/src/test/java/cz/zcu/kiv/aswi/fulltextsearch/HttpRequestTest.java b/be/fulltextsearch/src/test/java/cz/zcu/kiv/aswi/fulltextsearch/HttpRequestTest.java
index 57054b0..a57250e 100644
--- a/be/fulltextsearch/src/test/java/cz/zcu/kiv/aswi/fulltextsearch/HttpRequestTest.java
+++ b/be/fulltextsearch/src/test/java/cz/zcu/kiv/aswi/fulltextsearch/HttpRequestTest.java
@@ -26,9 +26,4 @@ public class HttpRequestTest {
                 String.class)).contains(controller.info());
     }
 
-    @Test
-    public void addTest() throws Exception {
-        assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/add",
-                String.class)).contains(controller.add());
-    }
 }
\ No newline at end of file
