PageRenderTime 94ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main/java/org/elasticsearch/plugin/infinitbyte/PartialUpdateRestAction.java

https://github.com/medcl/elasticsearch-partialupdate
Java | 408 lines | 345 code | 49 blank | 14 comment | 46 complexity | dd2fa15c7a08ea5e5d9b731a4e36e3de MD5 | raw file
  1. package org.elasticsearch.plugin.infinitbyte;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONArray;
  4. import com.alibaba.fastjson.JSONObject;
  5. import org.elasticsearch.ElasticsearchParseException;
  6. import org.elasticsearch.action.ActionListener;
  7. import org.elasticsearch.action.WriteConsistencyLevel;
  8. import org.elasticsearch.action.get.GetRequest;
  9. import org.elasticsearch.action.get.GetResponse;
  10. import org.elasticsearch.action.index.IndexRequest;
  11. import org.elasticsearch.action.index.IndexResponse;
  12. import org.elasticsearch.action.support.replication.ReplicationType;
  13. import org.elasticsearch.client.Client;
  14. import org.elasticsearch.common.bytes.BytesReference;
  15. import org.elasticsearch.common.compress.lzf.LZFChunk;
  16. import org.elasticsearch.common.inject.Inject;
  17. import org.elasticsearch.common.io.stream.BytesStreamInput;
  18. import org.elasticsearch.common.io.stream.CachedStreamInput;
  19. import org.elasticsearch.common.io.stream.HandlesStreamInput;
  20. import org.elasticsearch.common.settings.Settings;
  21. import org.elasticsearch.common.xcontent.*;
  22. import org.elasticsearch.index.VersionType;
  23. import org.elasticsearch.rest.*;
  24. import org.elasticsearch.rest.action.support.RestActions;
  25. import org.elasticsearch.rest.action.support.RestXContentBuilder;
  26. import java.io.IOException;
  27. import java.util.Iterator;
  28. import java.util.Map;
  29. import static org.elasticsearch.rest.RestStatus.*;
  30. import static org.elasticsearch.rest.action.support.RestXContentBuilder.restContentBuilder;
  31. /**
  32. * Created by IntelliJ IDEA. User: Medcl' Date: 9/15/11 Time: 3:09 PM
  33. */
  34. public class PartialUpdateRestAction extends BaseRestHandler {
  35. @Inject
  36. public PartialUpdateRestAction(Settings settings, Client client,
  37. RestController restController) {
  38. super(settings, client);
  39. restController.registerHandler(RestRequest.Method.POST,"/{index}/{type}/{id}/_partial_update", this);
  40. restController.registerHandler(RestRequest.Method.PUT,"/{index}/{type}/{id}/_partial_update", this);
  41. restController.registerHandler(RestRequest.Method.POST,"/{index}/{type}/{id}/_partial_update/{array_merge}", this);
  42. restController.registerHandler(RestRequest.Method.PUT,"/{index}/{type}/{id}/_partial_update/{array_merge}", this);
  43. }
  44. public void handleRequest(final RestRequest request,
  45. final RestChannel channel) {
  46. if (logger.isDebugEnabled()) {
  47. logger.debug("partial update entering...");
  48. }
  49. if (logger.isDebugEnabled()) {
  50. logger.debug("doc pending to be update:{}/{}/{}",
  51. request.param("index"), request.param("type"),
  52. request.param("id"));
  53. }
  54. final String mergeMethod=request.param("array_merge","replace");
  55. final String sourceJson=sourceAsString(request.content());
  56. final JSONObject pendingChangeJsonObject=JSON.parseObject(sourceJson);
  57. // if pending changes is empty,just return
  58. if (pendingChangeJsonObject.size() <= 0) {
  59. XContentBuilder builder = null;
  60. try {
  61. builder = RestXContentBuilder.restContentBuilder(request);
  62. builder.startObject()
  63. .field("reason", "pending changes is empty")
  64. .endObject();
  65. channel.sendResponse(new XContentRestResponse(request,
  66. RestStatus.BAD_REQUEST, builder));
  67. return;
  68. } catch (IOException e) {
  69. logger.error("update",e);
  70. }
  71. }
  72. final GetRequest getRequest = new GetRequest(request.param("index"),
  73. request.param("type"), request.param("id"));
  74. getRequest.routing(request.param("routing"));
  75. getRequest.preference(request.param("preference"));
  76. // getRequest.realtime(request.paramAsBoolean("realtime", null));
  77. // no need to have a threaded listener since we just send back a
  78. // response
  79. getRequest.listenerThreaded(false);
  80. // if we have a local operation, execute it on a thread since we don't
  81. // spawn
  82. getRequest.operationThreaded(true);
  83. // get original document
  84. client.get(getRequest, new ActionListener<GetResponse>() {
  85. public void onResponse(GetResponse getResponse) {
  86. if (logger.isDebugEnabled()) {
  87. logger.debug("entering get response...");
  88. }
  89. try {
  90. XContentBuilder builder = restContentBuilder(request);
  91. getResponse.toXContent(builder, request);
  92. if (!getResponse.isExists()) {
  93. channel.sendResponse(new XContentRestResponse(request,
  94. NOT_FOUND, builder));
  95. } else {
  96. if (logger.isDebugEnabled()) {
  97. logger.debug(getResponse.getSourceAsString());
  98. }
  99. if (!getResponse.isSourceEmpty()) {
  100. String source= getResponse.getSourceAsString();
  101. logger.debug("origin:"+source);
  102. logger.debug("pending:"+sourceJson);
  103. JSONObject jsonObject = JSON.parseObject(source);
  104. // prepare document
  105. Iterator<String> iterator;
  106. for (iterator=pendingChangeJsonObject.keySet().iterator(); iterator.hasNext();) {
  107. String next = iterator.next();
  108. logger.debug("update:"+next);
  109. if(jsonObject.containsKey(next)){
  110. Object jo = jsonObject.get(next);
  111. if(jo instanceof JSONArray){
  112. JSONArray jArray = ((JSONArray) jo);
  113. Object v=pendingChangeJsonObject.get(next);
  114. boolean pendingDataIsArray=false;
  115. JSONArray pArray=null;
  116. String trim = v.toString().trim();
  117. if(trim.startsWith("[")&& trim.endsWith("]")){
  118. try{
  119. pArray =JSON.parseArray(trim);
  120. if(pArray.size()>0){
  121. pendingDataIsArray=true;
  122. }
  123. }catch (Exception e){
  124. logger.error("update",e);
  125. }
  126. }
  127. if(mergeMethod.equals("append")){
  128. if(pendingDataIsArray){
  129. for (int i = 0; i < pArray.size(); i++) {
  130. if(!jArray.contains(pArray.get(i))){
  131. jArray.add(pArray.get(i));
  132. }
  133. }
  134. } else{
  135. jArray.add(v);
  136. }
  137. jsonObject.put(next,jArray);
  138. }else if(mergeMethod.equals("remove")){
  139. if(pendingDataIsArray){
  140. for (Object aPArray : pArray) {
  141. if (jArray.contains(aPArray)) {
  142. jArray.remove(aPArray);
  143. }
  144. }
  145. }else{
  146. if(jArray.contains(v)){
  147. jArray.remove(v);
  148. }
  149. }
  150. jsonObject.put(next,jArray);
  151. }else{
  152. jsonObject.put(next,pendingChangeJsonObject.get(next));
  153. }
  154. }else{
  155. jsonObject.put(next,pendingChangeJsonObject.get(next));
  156. }
  157. }else{
  158. jsonObject.put(next,pendingChangeJsonObject.get(next));
  159. }
  160. }
  161. long epoch = System.currentTimeMillis()/1000;
  162. jsonObject.put("_last_partial_updated", epoch);
  163. logger.debug("update json:"+jsonObject.toJSONString());
  164. // indexing
  165. IndexRequest indexRequest = new IndexRequest(
  166. request.param("index"), request
  167. .param("type"), request.param("id"));
  168. indexRequest.routing(request.param("routing"));
  169. indexRequest.parent(request.param("parent"));
  170. indexRequest.source(jsonObject.toJSONString());
  171. indexRequest.timeout(request.paramAsTime("timeout",
  172. IndexRequest.DEFAULT_TIMEOUT));
  173. indexRequest.refresh(request.paramAsBoolean(
  174. "refresh", indexRequest.refresh()));
  175. indexRequest.version(RestActions
  176. .parseVersion(request));
  177. indexRequest.versionType(VersionType.fromString(
  178. request.param("version_type"),
  179. indexRequest.versionType()));
  180. indexRequest.opType(IndexRequest.OpType.INDEX);
  181. String replicationType = request
  182. .param("replication");
  183. if (replicationType != null) {
  184. indexRequest.replicationType(ReplicationType
  185. .fromString(replicationType));
  186. }
  187. String consistencyLevel = request
  188. .param("consistency");
  189. if (consistencyLevel != null) {
  190. indexRequest
  191. .consistencyLevel(WriteConsistencyLevel
  192. .fromString(consistencyLevel));
  193. }
  194. // we just send a response, no need to fork
  195. indexRequest.listenerThreaded(false);
  196. // we don't spawn, then fork if local
  197. indexRequest.operationThreaded(true);
  198. if (logger.isDebugEnabled()) {
  199. logger.debug("ready to indexing");
  200. }
  201. client.index(indexRequest,
  202. new ActionListener<IndexResponse>() {
  203. public void onResponse(
  204. IndexResponse response) {
  205. if (logger.isDebugEnabled()) {
  206. logger.debug("entering index response...");
  207. }
  208. try {
  209. XContentBuilder builder = RestXContentBuilder
  210. .restContentBuilder(request);
  211. builder.startObject()
  212. .field(Fields.OK, true)
  213. .field(Fields._INDEX,
  214. response.getIndex())
  215. .field(Fields._TYPE,
  216. response.getType())
  217. .field(Fields._ID,
  218. response.getId())
  219. .field(Fields._VERSION,
  220. response.getVersion());
  221. builder.endObject();
  222. RestStatus status = OK;
  223. if (response.getVersion() == 1) {
  224. status = CREATED;
  225. }
  226. channel.sendResponse(new XContentRestResponse(
  227. request, status,
  228. builder));
  229. } catch (Exception e) {
  230. onFailure(e);
  231. }
  232. if (logger.isDebugEnabled()) {
  233. logger.debug("exit index response");
  234. }
  235. }
  236. public void onFailure(Throwable e) {
  237. try {
  238. channel.sendResponse(new XContentThrowableRestResponse(
  239. request, e));
  240. } catch (IOException e1) {
  241. logger.error(
  242. "Failed to send failure response",
  243. e1);
  244. }
  245. }
  246. });
  247. } else {
  248. builder = RestXContentBuilder
  249. .restContentBuilder(request);
  250. builder.startObject()
  251. .field("reason", "source is empty")
  252. .endObject();
  253. channel.sendResponse(new XContentRestResponse(
  254. request, RestStatus.BAD_REQUEST, builder));
  255. }
  256. }
  257. } catch (Exception e) {
  258. onFailure(e);
  259. }
  260. }
  261. public void onFailure(Throwable e) {
  262. try {
  263. channel.sendResponse(new XContentThrowableRestResponse(
  264. request, e));
  265. } catch (IOException e1) {
  266. e1.printStackTrace();
  267. logger.error("failed to send failure response", e1);
  268. }
  269. }
  270. });
  271. if (logger.isDebugEnabled()) {
  272. logger.debug("exit partial update");
  273. }
  274. }
  275. public String sourceAsString(BytesReference source ) {
  276. if(source!=null){
  277. try {
  278. return XContentHelper.convertToJson(source, false);
  279. } catch (IOException e) {
  280. throw new ElasticsearchParseException("failed to convert source to a json string");
  281. }
  282. }
  283. return null;
  284. }
  285. public static Map<String, Object> sourceAsMap(byte[] bytes, int offset,
  286. int length) {
  287. XContentParser parser = null;
  288. try {
  289. if (isCompressed(bytes, offset, length)) {
  290. BytesStreamInput siBytes = new BytesStreamInput(bytes, offset,
  291. length, true);
  292. HandlesStreamInput siLzf = CachedStreamInput
  293. .cachedHandles(siBytes);
  294. XContentType contentType = XContentFactory.xContentType(siLzf);
  295. siLzf.reset();
  296. parser = XContentFactory.xContent(contentType).createParser(
  297. siLzf);
  298. return parser.map();
  299. } else {
  300. parser = XContentFactory.xContent(bytes, offset, length)
  301. .createParser(bytes, offset, length);
  302. return parser.map();
  303. }
  304. } catch (Exception e) {
  305. throw new ElasticsearchParseException(
  306. "Failed to parse source to map", e);
  307. } finally {
  308. if (parser != null) {
  309. parser.close();
  310. }
  311. }
  312. }
  313. public static Map<String, Object> sourceAsMap(byte[] bytes, int offset,
  314. int length, boolean unsafe) {
  315. XContentParser parser = null;
  316. try {
  317. if (isCompressed(bytes, offset, length)) {
  318. BytesStreamInput siBytes = new BytesStreamInput(bytes, offset,
  319. length, unsafe);
  320. HandlesStreamInput siLzf = CachedStreamInput
  321. .cachedHandles(siBytes);
  322. XContentType contentType = XContentFactory.xContentType(siLzf);
  323. siLzf.reset();
  324. parser = XContentFactory.xContent(contentType).createParser(
  325. siLzf);
  326. return parser.map();
  327. } else {
  328. parser = XContentFactory.xContent(bytes, offset, length)
  329. .createParser(bytes, offset, length);
  330. return parser.map();
  331. }
  332. } catch (Exception e) {
  333. throw new ElasticsearchParseException(
  334. "Failed to parse source to map", e);
  335. } finally {
  336. if (parser != null) {
  337. parser.close();
  338. }
  339. }
  340. }
  341. public static boolean isCompressed(final byte[] buffer, int offset,
  342. int length) {
  343. return length >= 2 && buffer[offset] == LZFChunk.BYTE_Z
  344. && buffer[offset + 1] == LZFChunk.BYTE_V;
  345. }
  346. static final class Fields {
  347. static final XContentBuilderString OK = new XContentBuilderString("ok");
  348. static final XContentBuilderString _INDEX = new XContentBuilderString(
  349. "_index");
  350. static final XContentBuilderString _TYPE = new XContentBuilderString(
  351. "_type");
  352. static final XContentBuilderString _ID = new XContentBuilderString(
  353. "_id");
  354. static final XContentBuilderString _VERSION = new XContentBuilderString(
  355. "_version");
  356. static final XContentBuilderString MATCHES = new XContentBuilderString(
  357. "matches");
  358. }
  359. }