Adding initial working engine.
This commit is contained in:
commit
92c502bc45
|
@ -0,0 +1,36 @@
|
|||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>info.burguillos</groupId>
|
||||
<artifactId>search</artifactId>
|
||||
<version>1</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-core</artifactId>
|
||||
<version>9.7.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-queryparser</artifactId>
|
||||
<version>9.7.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,12 @@
|
|||
package info.burguillos.search;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
public static void main(String[] args) {
|
||||
ApplicationContext context = SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
package info.burguillos.search;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.NIOFSDirectory;
|
||||
import org.apache.lucene.store.NativeFSLockFactory;
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
|
||||
import org.apache.lucene.queryparser.classic.QueryParser;
|
||||
import org.apache.lucene.queryparser.classic.ParseException;
|
||||
|
||||
class ResponseBool {
|
||||
private boolean ok;
|
||||
private String reason;
|
||||
public String getreason() {
|
||||
return reason;
|
||||
}
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
public boolean getOk() {
|
||||
return ok;
|
||||
}
|
||||
public void setOk(boolean ok) {
|
||||
this.ok = ok;
|
||||
}
|
||||
}
|
||||
|
||||
class SearchObject {
|
||||
private String title;
|
||||
private String author;
|
||||
private String content;
|
||||
private String url;
|
||||
private String urlImage;
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
public String getUrlImage() {
|
||||
return urlImage;
|
||||
}
|
||||
public void setUrlImage(String urlImage) {
|
||||
this.urlImage = urlImage;
|
||||
}
|
||||
}
|
||||
|
||||
class ResponseSearch extends ResponseBool {
|
||||
private List<SearchObject> searchObjects;
|
||||
public List<SearchObject> getSearchObjects() {
|
||||
return searchObjects;
|
||||
}
|
||||
public void setSearchObjects(List<SearchObject> searchObjects) {
|
||||
this.searchObjects = searchObjects;
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
class LoadContentController {
|
||||
@GetMapping("/")
|
||||
public ResponseBool root() {
|
||||
ResponseBool response = new ResponseBool();
|
||||
response.setOk(true);
|
||||
return response;
|
||||
}
|
||||
|
||||
private ResponseBool returnFail(String reason) {
|
||||
ResponseBool response = new ResponseBool();
|
||||
response.setOk(false);
|
||||
response.setReason(reason);
|
||||
return response;
|
||||
}
|
||||
|
||||
@GetMapping(value="/search/{indexName}")
|
||||
public ResponseBool search(@PathVariable String indexName,
|
||||
@RequestParam(name = "q") String searchQuery) {
|
||||
if (indexName == null) {
|
||||
return returnFail("No queryString.");
|
||||
}
|
||||
if (indexName.contains("..") || indexName.contains("/")) {
|
||||
return returnFail("Invalid indexName.");
|
||||
}
|
||||
Path pathIndex = FileSystems.getDefault().getPath("indexes", indexName);
|
||||
StandardAnalyzer analyzer = new StandardAnalyzer();
|
||||
if (!Files.exists(pathIndex, LinkOption.NOFOLLOW_LINKS)) {
|
||||
return returnFail("No such index.");
|
||||
}
|
||||
try {
|
||||
Directory index = new NIOFSDirectory(pathIndex, NativeFSLockFactory.INSTANCE);
|
||||
Query q = new MultiFieldQueryParser(new String[]{"title", "content"}, analyzer).parse(searchQuery);
|
||||
IndexReader reader = DirectoryReader.open(index);
|
||||
IndexSearcher searcher = new IndexSearcher(reader);
|
||||
TopDocs docs = searcher.search(q, 10);
|
||||
ScoreDoc[] hits = docs.scoreDocs;
|
||||
List<SearchObject> searchObjects = new ArrayList<SearchObject>();
|
||||
for (int i = 0; i < hits.length; ++i) {
|
||||
int docId = hits[i].doc;
|
||||
Document d = searcher.doc(docId);
|
||||
SearchObject searchObject = readDocument(d);
|
||||
searchObjects.add(searchObject);
|
||||
}
|
||||
ResponseSearch responseSearch = new ResponseSearch();
|
||||
responseSearch.setSearchObjects(searchObjects);
|
||||
responseSearch.setOk(true);
|
||||
return responseSearch;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return returnFail("Failed to read index.");
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
return returnFail("Unable to understand query.");
|
||||
}
|
||||
}
|
||||
|
||||
public static SearchObject readDocument(Document d) {
|
||||
SearchObject searchObject = new SearchObject();
|
||||
searchObject.setTitle(d.get("title"));
|
||||
String author = d.get("author");
|
||||
if (author != null) {
|
||||
searchObject.setAuthor(author);
|
||||
}
|
||||
String content = d.get("content");
|
||||
if (content.length() > 500) {
|
||||
content = content.substring(0, 500);
|
||||
}
|
||||
searchObject.setContent(content);
|
||||
searchObject.setUrl(d.get("url"));
|
||||
String urlImage = d.get("urlImage");
|
||||
if (urlImage != null) {
|
||||
searchObject.setUrlImage(urlImage);
|
||||
}
|
||||
return searchObject;
|
||||
}
|
||||
|
||||
@PutMapping(value="/index/{indexName}")
|
||||
public ResponseBool create_index(@PathVariable String indexName, @RequestBody List<SearchObject> searchObjects) {
|
||||
try {
|
||||
if (indexName == null) {
|
||||
return returnFail("No indexName passed.");
|
||||
}
|
||||
if (indexName.contains("..") || indexName.contains("/")) {
|
||||
return returnFail("Invalid indexName.");
|
||||
}
|
||||
Path pathIndex = FileSystems.getDefault().getPath("indexes", indexName);
|
||||
StandardAnalyzer analyzer = new StandardAnalyzer();
|
||||
if (Files.exists(pathIndex, LinkOption.NOFOLLOW_LINKS)) {
|
||||
FileSystemUtils.deleteRecursively(pathIndex);
|
||||
}
|
||||
Files.createDirectories(pathIndex);
|
||||
Directory index = new NIOFSDirectory(pathIndex, NativeFSLockFactory.INSTANCE);
|
||||
|
||||
IndexWriterConfig config = new IndexWriterConfig(analyzer);
|
||||
|
||||
IndexWriter w = new IndexWriter(index, config);
|
||||
for (SearchObject searchObject : searchObjects) {
|
||||
addDoc(w, searchObject);
|
||||
}
|
||||
w.close();
|
||||
ResponseBool response = new ResponseBool();
|
||||
response.setOk(true);
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return returnFail("I/O Error.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void addDoc(IndexWriter w, SearchObject searchObject) throws IOException {
|
||||
Document doc = new Document();
|
||||
doc.add(new TextField("title", searchObject.getTitle(), Field.Store.YES));
|
||||
if (searchObject.getAuthor() != null) {
|
||||
doc.add(new TextField("author", searchObject.getAuthor(), Field.Store.YES));
|
||||
}
|
||||
doc.add(new TextField("content", searchObject.getContent(), Field.Store.YES));
|
||||
doc.add(new TextField("url", searchObject.getUrl(), Field.Store.YES));
|
||||
if (searchObject.getUrlImage() != null) {
|
||||
doc.add(new TextField("urlImage", searchObject.getUrlImage(), Field.Store.YES));
|
||||
}
|
||||
w.addDocument(doc);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue