mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#584 - ElasticSearch: Internal changes for config, expressions and JSON conversion
This commit is contained in:
@@ -3,40 +3,43 @@ package com.avaje.ebean.config;
|
||||
import com.avaje.ebean.annotation.DocStoreEvent;
|
||||
|
||||
/**
|
||||
* Configuration for the Document store (ElasticSearch) integration.
|
||||
* Configuration for the Document store integration (e.g. ElasticSearch).
|
||||
*/
|
||||
public class DocStoreConfig {
|
||||
|
||||
/**
|
||||
* True when the Document store integration is active/on.
|
||||
*/
|
||||
boolean active;
|
||||
protected boolean active;
|
||||
|
||||
/**
|
||||
* When true the Document store should drop and re-create any document mapping (like DDL).
|
||||
*/
|
||||
boolean dropCreate;
|
||||
protected boolean dropCreate;
|
||||
|
||||
/**
|
||||
* The URL of the Document store server. For example: http://localhost:9200.
|
||||
*/
|
||||
String url;
|
||||
protected String url;
|
||||
|
||||
/**
|
||||
* The default mode used by indexes.
|
||||
*/
|
||||
DocStoreEvent persist = DocStoreEvent.UPDATE;
|
||||
protected DocStoreEvent persist = DocStoreEvent.UPDATE;
|
||||
|
||||
/**
|
||||
* The default batch size to use for the Bulk API calls.
|
||||
*/
|
||||
int bulkBatchSize = 1000;
|
||||
|
||||
protected int bulkBatchSize = 1000;
|
||||
|
||||
/**
|
||||
* Return true if the Document store (ElasticSearch) integration is active.
|
||||
*/
|
||||
public boolean isActive() {
|
||||
String systemValue = System.getProperty("ebean.docstore.active");
|
||||
if (systemValue != null) {
|
||||
return Boolean.parseBoolean(systemValue);
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
@@ -47,6 +50,27 @@ public class DocStoreConfig {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL to the Document store.
|
||||
*/
|
||||
public String getUrl() {
|
||||
String systemValue = System.getProperty("ebean.docstore.url");
|
||||
if (systemValue != null) {
|
||||
return systemValue;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL to the Document store server.
|
||||
* <p>
|
||||
* For a local ElasticSearch server this would be: http://localhost:9200
|
||||
*/
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the document store should recreate mappings.
|
||||
*/
|
||||
@@ -61,6 +85,24 @@ public class DocStoreConfig {
|
||||
this.dropCreate = dropCreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default batch size to use for calls to the Bulk API.
|
||||
*/
|
||||
public int getBulkBatchSize() {
|
||||
return bulkBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default batch size to use for calls to the Bulk API.
|
||||
* <p>
|
||||
* The batch size can be set on a transaction via {@link com.avaje.ebean.Transaction#setDocStoreUpdateBatchSize(int)}.
|
||||
* </p>
|
||||
*/
|
||||
public void setBulkBatchSize(int bulkBatchSize) {
|
||||
this.bulkBatchSize = bulkBatchSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the default behavior for when Insert, Update and Delete events occur on beans that have an associated
|
||||
* Document store.
|
||||
@@ -91,40 +133,6 @@ public class DocStoreConfig {
|
||||
this.persist = persist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL to the Document store.
|
||||
*/
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL to the Document store server.
|
||||
*
|
||||
* For a local ElasticSearch server this would be: http://localhost:9200
|
||||
*/
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the default batch size to use for calls to the Bulk API.
|
||||
*/
|
||||
public int getBulkBatchSize() {
|
||||
return bulkBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default batch size to use for calls to the Bulk API.
|
||||
* <p>
|
||||
* The batch size can be set on a transaction via {@link com.avaje.ebean.Transaction#setDocStoreUpdateBatchSize(int)}.
|
||||
* </p>
|
||||
*/
|
||||
public void setBulkBatchSize(int bulkBatchSize) {
|
||||
this.bulkBatchSize = bulkBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings specified in properties files.
|
||||
*/
|
||||
|
||||
@@ -1129,7 +1129,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setType(Type.BEAN);
|
||||
|
||||
if (SpiQuery.Mode.NORMAL.equals(spiQuery.getMode()) && !spiQuery.isLoadBeanCache()) {
|
||||
// See if we can skip doing the fetch completely by getting the bean from the
|
||||
// persistence context or the bean cache
|
||||
@@ -1140,6 +1139,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(spiQuery, t);
|
||||
if (request.isUseDocStore()) {
|
||||
return docStore().getById(query.getBeanType(), query.getId());
|
||||
}
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return (T) request.findId();
|
||||
@@ -1158,9 +1160,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return findId(query, t);
|
||||
}
|
||||
|
||||
BeanDescriptor<T> desc = beanDescriptorManager.getBeanDescriptor(query.getBeanType());
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
BeanDescriptor<T> desc = spiQuery.getBeanDescriptor();
|
||||
|
||||
T bean = desc.cacheNaturalKeyLookup((SpiQuery<T>) query, (SpiTransaction) t);
|
||||
T bean = desc.cacheNaturalKeyLookup(spiQuery, (SpiTransaction) t);
|
||||
if (bean != null) {
|
||||
return bean;
|
||||
}
|
||||
@@ -1352,6 +1355,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, t);
|
||||
|
||||
if (request.isUseDocStore()) {
|
||||
docStore().findEach(query, consumer);
|
||||
return;
|
||||
}
|
||||
|
||||
request.initTransIfRequired();
|
||||
request.findEach(consumer);
|
||||
// no try finally - findVisit guarantee's cleanup of the transaction if required
|
||||
@@ -1397,7 +1405,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
public <T> List<T> findList(Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
|
||||
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
return (List<T>) result;
|
||||
|
||||
@@ -462,7 +462,7 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
|
||||
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain,
|
||||
boolean propertyDeploy) {
|
||||
throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName());
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-11
@@ -20,17 +20,7 @@ class CaseInsensitiveEqualExpression extends AbstractExpression {
|
||||
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
String[] values = value.split(" ");
|
||||
if (values.length == 1) {
|
||||
context.writeMatch(propName, value);
|
||||
} else {
|
||||
context.writeBoolStart(true);
|
||||
for (String val : values) {
|
||||
context.writeMatch(propName, val);
|
||||
}
|
||||
context.writeBoolEnd();
|
||||
}
|
||||
context.writeIEqual(propName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+140
-19
@@ -36,6 +36,9 @@ public class ElasticExpressionContext {
|
||||
|
||||
private String currentNestedPath;
|
||||
|
||||
/**
|
||||
* Construct given the JSON generator and root bean type.
|
||||
*/
|
||||
public ElasticExpressionContext(JsonGenerator json, BeanType<?> desc) {
|
||||
this.json = json;
|
||||
this.desc = desc;
|
||||
@@ -66,23 +69,38 @@ public class ElasticExpressionContext {
|
||||
|
||||
/**
|
||||
* Return an associated 'raw' property given the property name.
|
||||
* This just returns the original propertyName if no 'raw' property is mapped.
|
||||
*/
|
||||
private String rawProperty(String propertyName) {
|
||||
return desc.docStore().rawProperty(propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Bool MUST or SHOULD.
|
||||
*
|
||||
* If conjunction is true then MUST(and) and if false is SHOULD(or).
|
||||
*/
|
||||
public void writeBoolStart(boolean conjunction) throws IOException {
|
||||
writeBoolStart((conjunction) ? MUST : SHOULD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Bool MUST.
|
||||
*/
|
||||
public void writeBoolMustStart() throws IOException {
|
||||
writeBoolStart(MUST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Bool MUST_NOT.
|
||||
*/
|
||||
public void writeBoolMustNotStart() throws IOException {
|
||||
writeBoolStart(MUST_NOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a Bool expression list with the given type (MUST, MUST_NOT, SHOULD).
|
||||
*/
|
||||
private void writeBoolStart(String type) throws IOException {
|
||||
endNested();
|
||||
json.writeStartObject();
|
||||
@@ -90,17 +108,28 @@ public class ElasticExpressionContext {
|
||||
json.writeArrayFieldStart(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the end of a Bool expression list.
|
||||
*/
|
||||
public void writeBoolEnd() throws IOException {
|
||||
json.writeEndArray();
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a term expression.
|
||||
*/
|
||||
public void writeTerm(String propertyName, Object value) throws IOException {
|
||||
|
||||
writeRawType(TERM, rawProperty(propertyName), value);
|
||||
// prepareNested on propertyName and expression uses raw
|
||||
prepareNestedPath(propertyName);
|
||||
writeRawExpression(TERM, rawProperty(propertyName), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a range expression with a single value.
|
||||
*/
|
||||
public void writeRange(String propertyName, String rangeType, Object value) throws IOException {
|
||||
|
||||
prepareNestedPath(propertyName);
|
||||
@@ -114,8 +143,14 @@ public class ElasticExpressionContext {
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a range expression with a low and high value.
|
||||
*/
|
||||
public void writeRange(String propertyName, Op lowOp, Object valueLow, Op highOp, Object valueHigh) throws IOException {
|
||||
|
||||
//Property property = desc.getProperty(propertyName);
|
||||
//property.
|
||||
|
||||
prepareNestedPath(propertyName);
|
||||
json.writeStartObject();
|
||||
json.writeObjectFieldStart(RANGE);
|
||||
@@ -129,6 +164,9 @@ public class ElasticExpressionContext {
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a terms expression.
|
||||
*/
|
||||
public void writeTerms(String propertyName, Object[] values) throws IOException {
|
||||
|
||||
prepareNestedPath(propertyName);
|
||||
@@ -143,7 +181,9 @@ public class ElasticExpressionContext {
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write an Ids expression.
|
||||
*/
|
||||
public void writeIds(List<?> idList) throws IOException {
|
||||
|
||||
endNested();
|
||||
@@ -158,6 +198,9 @@ public class ElasticExpressionContext {
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an Id expression.
|
||||
*/
|
||||
public void writeId(Object value) throws IOException {
|
||||
|
||||
List<Object> ids = new ArrayList<Object>(1);
|
||||
@@ -165,33 +208,89 @@ public class ElasticExpressionContext {
|
||||
writeIds(ids);
|
||||
}
|
||||
|
||||
public void writeSuffix(String propertyName, String value) {
|
||||
throw new IllegalArgumentException("Not implemented yet. Could search for a mapped 'reversed' property and do prefix query");
|
||||
}
|
||||
|
||||
public void writePrefix(String propertyName, String value) throws IOException {
|
||||
/**
|
||||
* Write a prefix expression.
|
||||
*/
|
||||
public void writeStartsWith(String propertyName, String value) throws IOException {
|
||||
// use analysed field
|
||||
prepareNestedPath(propertyName);
|
||||
writeRawType(PREFIX, propertyName, value);
|
||||
writeRawWithPrepareNested(PREFIX, propertyName, value.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Suffix expression not supported yet.
|
||||
*/
|
||||
public void writeEndsWith(String propertyName, String value) throws IOException {
|
||||
// use analysed field
|
||||
// this will likely be slow - best to avoid if you can
|
||||
writeWildcard(propertyName, "*" + value.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a match expression.
|
||||
*/
|
||||
public void writeContains(String propertyName, String value) throws IOException {
|
||||
// use analysed field
|
||||
writeWildcard(propertyName, "*" + value.toLowerCase() + "*");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a wildcard expression.
|
||||
*/
|
||||
public void writeLike(String propertyName, String value) throws IOException {
|
||||
// use analysed field
|
||||
String val = value.toLowerCase();
|
||||
// replace SQL wildcard characters with ElasticSearch ones
|
||||
val = val.replace('_', '?');
|
||||
val = val.replace('%', '*');
|
||||
writeRawWithPrepareNested(WILDCARD, propertyName, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write case-insensitive equal to.
|
||||
*/
|
||||
public void writeIEqual(String propName, String value) throws IOException {
|
||||
|
||||
String[] values = value.toLowerCase().split(" ");
|
||||
if (values.length == 1) {
|
||||
writeMatch(propName, value);
|
||||
} else {
|
||||
// Boolean AND all the terms together
|
||||
writeBoolStart(true);
|
||||
for (String val : values) {
|
||||
writeMatch(propName, val);
|
||||
}
|
||||
writeBoolEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a prefix expression.
|
||||
*/
|
||||
public void writeMatch(String propertyName, String value) throws IOException {
|
||||
// use analysed field
|
||||
prepareNestedPath(propertyName);
|
||||
writeRawType(MATCH, propertyName, value);
|
||||
writeRawWithPrepareNested(MATCH, propertyName, value.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a wildcard expression.
|
||||
*/
|
||||
public void writeWildcard(String propertyName, String value) throws IOException {
|
||||
prepareNestedPath(propertyName);
|
||||
writeRawType(WILDCARD, propertyName, value);
|
||||
writeRawWithPrepareNested(WILDCARD, propertyName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write raw JSON to the query buffer.
|
||||
*/
|
||||
public void writeRaw(String jsonExpression) throws IOException {
|
||||
json.writeRaw(jsonExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an exists expression.
|
||||
*/
|
||||
public void writeExists(boolean notNull, String propertyName) throws IOException {
|
||||
|
||||
// prepareNestedPath prior to BoolMustNotStart
|
||||
prepareNestedPath(propertyName);
|
||||
if (!notNull) {
|
||||
writeBoolMustNotStart();
|
||||
@@ -203,12 +302,23 @@ public class ElasticExpressionContext {
|
||||
}
|
||||
|
||||
private void writeExists(String propertyName) throws IOException {
|
||||
writeRawType(EXISTS, FIELD, propertyName);
|
||||
writeRawExpression(EXISTS, FIELD, propertyName);
|
||||
}
|
||||
|
||||
private void writeRawType(String type, String propertyName, Object value) throws IOException {
|
||||
/**
|
||||
* Write with prepareNestedPath() on the propertyName
|
||||
*/
|
||||
private void writeRawWithPrepareNested(String type, String propertyName, Object value) throws IOException {
|
||||
|
||||
prepareNestedPath(propertyName);
|
||||
writeRawExpression(type, propertyName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write raw. prepareNestedPath() should already be done.
|
||||
*/
|
||||
private void writeRawExpression(String type, String propertyName, Object value) throws IOException {
|
||||
|
||||
json.writeStartObject();
|
||||
json.writeObjectFieldStart(type);
|
||||
json.writeFieldName(propertyName);
|
||||
@@ -217,10 +327,12 @@ public class ElasticExpressionContext {
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write an expression for the core operations.
|
||||
*/
|
||||
public void writeSimple(Op type, String propertyName, Object value) throws IOException {
|
||||
|
||||
// prepareNested prior to boolMustNotStart
|
||||
prepareNestedPath(propertyName);
|
||||
switch (type) {
|
||||
case EQ:
|
||||
@@ -243,7 +355,6 @@ public class ElasticExpressionContext {
|
||||
default:
|
||||
writeRange(propertyName, type.docExp(), value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,6 +375,9 @@ public class ElasticExpressionContext {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we need to start a nested path filter and do so if required.
|
||||
*/
|
||||
private void prepareNestedPath(String propName) throws IOException {
|
||||
ExpressionPath exprPath = desc.getExpressionPath(propName);
|
||||
if (exprPath != null && exprPath.containsMany()) {
|
||||
@@ -274,6 +388,9 @@ public class ElasticExpressionContext {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a nested path filter.
|
||||
*/
|
||||
private void startNested(String nestedPath) throws IOException {
|
||||
|
||||
if (currentNestedPath != null) {
|
||||
@@ -281,6 +398,7 @@ public class ElasticExpressionContext {
|
||||
// just add to currentNestedPath
|
||||
return;
|
||||
} else {
|
||||
// end the prior one as this is different
|
||||
endNested();
|
||||
}
|
||||
}
|
||||
@@ -292,12 +410,15 @@ public class ElasticExpressionContext {
|
||||
json.writeFieldName("filter");
|
||||
}
|
||||
|
||||
/**
|
||||
* End a nested path filter if one is still open.
|
||||
*/
|
||||
private void endNested() throws IOException {
|
||||
if (currentNestedPath != null) {
|
||||
currentNestedPath = null;
|
||||
//json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
json.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,26 +28,29 @@ class LikeExpression extends AbstractExpression {
|
||||
@Override
|
||||
public void writeElastic(ElasticExpressionContext context) throws IOException {
|
||||
|
||||
String paramVal = (caseInsensitive) ? val.toLowerCase() : val;
|
||||
switch (type) {
|
||||
case RAW:
|
||||
context.writeWildcard(propName, paramVal);
|
||||
context.writeLike(propName, val);
|
||||
break;
|
||||
|
||||
case STARTS_WITH:
|
||||
context.writePrefix(propName, paramVal);
|
||||
context.writeStartsWith(propName, val);
|
||||
break;
|
||||
|
||||
case ENDS_WITH:
|
||||
context.writeSuffix(propName, paramVal);
|
||||
context.writeEndsWith(propName, val);
|
||||
break;
|
||||
|
||||
case CONTAINS:
|
||||
context.writeMatch(propName, paramVal);
|
||||
context.writeContains(propName, val);
|
||||
break;
|
||||
|
||||
case EQUAL_TO:
|
||||
context.writeTerm(propName, paramVal);
|
||||
if (caseInsensitive) {
|
||||
context.writeIEqual(propName, val);
|
||||
} else {
|
||||
context.writeTerm(propName, val);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -303,7 +303,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
try {
|
||||
writeElastic(context);
|
||||
context.flush();
|
||||
return sw.toString();
|
||||
generatedSql = sw.toString();
|
||||
return generatedSql;
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
|
||||
Reference in New Issue
Block a user