/*
* The MIT License
*
* Copyright 2015 jdlee.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jbake.app;
import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.OPartitionedDatabasePool;
import com.orientechnologies.orient.core.db.OPartitionedDatabasePoolFactory;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentPool;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OSchema;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.OCommandSQL;
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery;
import org.jbake.model.DocumentAttributes;
import org.jbake.model.DocumentTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author jdlee
*/
public class ContentStore {
private final Logger logger = LoggerFactory.getLogger(ContentStore.class);
private ODatabaseDocumentTx db;
private long start = -1;
private long limit = -1;
public ContentStore(final String type, String name) {
startupIfEnginesAreMissing();
db = new ODatabaseDocumentTx(type + ":" + name);
boolean exists = db.exists();
if (!exists) {
db.create();
}
db = new OPartitionedDatabasePoolFactory().get(type+":"+name, "admin", "admin").acquire();
ODatabaseRecordThreadLocal.INSTANCE.set(db);
if (!exists) {
updateSchema();
}
}
public long getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public long getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
public void resetPagination() {
this.start = -1;
this.limit = -1;
}
public final void updateSchema() {
OSchema schema = db.getMetadata().getSchema();
for (String docType : DocumentTypes.getDocumentTypes()) {
if (schema.getClass(docType) == null) {
createDocType(schema, docType);
}
}
if (schema.getClass("Signatures") == null) {
createSignatureType(schema);
}
}
public void close() {
db.close();
DBUtil.closeDataStore();
}
public void shutdown() {
Orient.instance().shutdown();
}
private void startupIfEnginesAreMissing() {
// If an instance of Orient was previously shutdown all engines are removed.
// We need to startup Orient again.
if ( Orient.instance().getEngines().size() == 0 ) {
Orient.instance().startup();
}
}
public void drop() {
db.drop();
}
public long getDocumentCount(String docType) {
return db.countClass(docType);
}
public long getPublishedCount(String docType) {
return (Long) query("select count(*) as count from " + docType + " where status='published'").get(0).get("count");
}
public DocumentList getDocumentStatus(String docType, String uri) {
return query("select sha1,rendered from " + docType + " where sourceuri=?", uri);
}
public DocumentList getPublishedPosts() {
return getPublishedContent("post");
}
public DocumentList getPublishedPostsByTag(String tag) {
return query("select * from post where status='published' and ? in tags order by date desc", tag);
}
public DocumentList getPublishedDocumentsByTag(String tag) {
final DocumentList documents = new DocumentList();
for (final String docType : DocumentTypes.getDocumentTypes()) {
DocumentList documentsByTag = query("select * from " + docType + " where status='published' and ? in tags order by date desc", tag);
documents.addAll(documentsByTag);
}
return documents;
}
public DocumentList getPublishedPages() {
return getPublishedContent("page");
}
public DocumentList getPublishedContent(String docType) {
String query = "select * from " + docType + " where status='published' order by date desc";
if ((start >= 0) && (limit > -1)) {
query += " SKIP " + start + " LIMIT " + limit;
}
return query(query);
}
public DocumentList getAllContent(String docType) {
String query = "select * from " + docType + " order by date desc";
if ((start >= 0) && (limit > -1)) {
query += " SKIP " + start + " LIMIT " + limit;
}
return query(query);
}
public DocumentList getAllTagsFromPublishedPosts() {
return query("select tags from post where status='published'");
}
public DocumentList getSignaturesForTemplates() {
return query("select sha1 from Signatures where key='templates'");
}
public DocumentList getUnrenderedContent(String docType) {
return query("select * from " + docType + " where rendered=false");
}
public void deleteContent(String docType, String uri) {
executeCommand("delete from " + docType + " where sourceuri=?", uri);
}
public void markConentAsRendered(String docType) {
executeCommand("update " + docType + " set rendered=true where rendered=false and cached=true");
}
public void updateSignatures(String currentTemplatesSignature) {
executeCommand("update Signatures set sha1=? where key='templates'", currentTemplatesSignature);
}
public void deleteAllByDocType(String docType) {
executeCommand("delete from " + docType);
}
public void insertSignature(String currentTemplatesSignature) {
executeCommand("insert into Signatures(key,sha1) values('templates',?)", currentTemplatesSignature);
}
private DocumentList query(String sql) {
List<ODocument> results = db.query(new OSQLSynchQuery<ODocument>(sql));
return DocumentList.wrap(results.iterator());
}
private DocumentList query(String sql, Object... args) {
List<ODocument> results = db.command(new OSQLSynchQuery<ODocument>(sql)).execute(args);
return DocumentList.wrap(results.iterator());
}
private void executeCommand(String query, Object... args) {
db.command(new OCommandSQL(query)).execute(args);
}
public Set<String> getTags() {
DocumentList docs = this.getAllTagsFromPublishedPosts();
Set<String> result = new HashSet<String>();
for (Map<String, Object> document : docs) {
String[] tags = DBUtil.toStringArray(document.get(Crawler.Attributes.TAGS));
Collections.addAll(result, tags);
}
return result;
}
public Set<String> getAllTags() {
Set<String> result = new HashSet<String>();
for (String docType : DocumentTypes.getDocumentTypes()) {
DocumentList docs = query("select tags from " + docType + " where status='published'");
for (Map<String, Object> document : docs) {
String[] tags = DBUtil.toStringArray(document.get(Crawler.Attributes.TAGS));
Collections.addAll(result, tags);
}
}
return result;
}
private void createDocType(final OSchema schema, final String doctype) {
logger.debug("Create document class '{}'", doctype );
OClass page = schema.createClass(doctype);
page.createProperty(String.valueOf(DocumentAttributes.SHA1), OType.STRING).setNotNull(true);
page.createIndex(doctype + "sha1Index", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.SHA1.toString());
page.createProperty(String.valueOf(DocumentAttributes.SOURCE_URI), OType.STRING).setNotNull(true);
page.createIndex(doctype + "sourceUriIndex", OClass.INDEX_TYPE.UNIQUE, DocumentAttributes.SOURCE_URI.toString());
page.createProperty(String.valueOf(DocumentAttributes.CACHED), OType.BOOLEAN).setNotNull(true);
page.createIndex(doctype + "cachedIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.CACHED.toString());
page.createProperty(String.valueOf(DocumentAttributes.RENDERED), OType.BOOLEAN).setNotNull(true);
page.createIndex(doctype + "renderedIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.RENDERED.toString());
page.createProperty(String.valueOf(DocumentAttributes.STATUS), OType.STRING).setNotNull(true);
page.createIndex(doctype + "statusIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.STATUS.toString());
}
private void createSignatureType(OSchema schema) {
OClass signatures = schema.createClass("Signatures");
signatures.createProperty(String.valueOf(DocumentAttributes.SHA1), OType.STRING).setNotNull(true);
signatures.createIndex("sha1Idx", OClass.INDEX_TYPE.UNIQUE, DocumentAttributes.SHA1.toString());
}
}