I'm trying to update data in Elastic Search in my Java program with TranportClient class. I know I can UPDATE in this way :
XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
.field("source.ivarId", source.ivarId)
.field("source.channel", source.channel)
.field("source.bacId", source.bacId).endObject();
UpdateResponse response = client.prepareUpdate(_index, _type, _id).setDoc(builder.string()).get();
while source is my user-defined class which contains 3 fields : ivarId, channel and bacId.
But I want to know is there any method that could do the same thing, but using another more efficient and easier way, so that I don't need to assign each field inside a class? For example, can I do like this?
XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
.field("source", source).endObject();
UpdateResponse response = client.prepareUpdate(_index, _type, _id).setDoc(builder.string()).get();
I tried the latter method, and I got this exception :
MapperParsingException[object mapping for [source] tried to parse field [source] as object, but found a concrete value]
I'm using Java 1.8.0.121, and both versions of ElasticSearch and TransportClient are 5.1. Thanks!
The answer is much more easier than I thought.
Gson gson = new Gson();
String sourceJsonString = gson.toJson(updateContent);
UpdateResponse response = client
.prepareUpdate(_index, "logs", id).setDoc(sourceJsonString).get();
updateContent is the object that contained new data, just to transform it to Json string, then use that to update, done.
Related
We have a Jackson ObjectReader that is overwriting any original data if it is missing from the json update request.
This is what the data structure looks like:
data class Model(
val fieldTypeA: FieldTypeA? = null,
)
data class FieldTypeA(
val valueA: String? = null,
val valueB: String? = null,
)
We then read an existing value from a database so that fieldTypeA.valueA = "Test"
val existingModel = repository.findById(id).getOrNull()
Then this line reads the existing data into an ObjectReader:
val readerForUpdating: ObjectReader = CustomMapper.readerForUpdating(existingEntity)
This is where the problem occurs. The readValue overwrites the fieldTypeA.valueA after this line is executed with a jsonRequest:
val updatedRequest: Model = readerForUpdating.readValue(jsonRequest)
jsonRequest:
{"fieldTypeA":{"valueB":"I am value B"}}
The existingEntity object now only contains fieldTypeA.valueB, with fieldTypeA.valueA getting overwritten with null.
Is there a way to tell Jackson not to overwrite when a value is missing from the JSON?
I've fixed my own problem in the following way, in case it helps someone in the future.
There was a new feature introduced in Jackson 2.9 which allows deep merging. To do this, the relevant property needs to be tagged with #JsonMerge.
So in my example question above, the Model object would be changed like this:
import com.fasterxml.jackson.annotation.JsonMerge
data class Model(
#JsonMerge
val fieldTypeA: FieldTypeA? = null,
)
This means if the original object contains a field with a value, it does not get overwritten to null when the new json comes in.
readerForUpdating(obj) only promises to use the root object you pass
Deserialization occurs normally except that the root-level value in JSON is not used for instantiating a new object; instead give updateable object is used as root
whereas fieldTypeA.valueA is an inner object.
It works on a shallow object:
val existingModel = FieldTypeA(valueA = "A")
println(existingModel)
val readerForUpdating: ObjectReader = MAPPER.readerForUpdating(existingModel)
val jsonRequest = "{\"valueB\":\"I am value B\"}"
val updatedRequest: FieldTypeA = readerForUpdating.readValue(jsonRequest)
println(updatedRequest)
produces
FieldTypeA(valueA=A, valueB=null)
FieldTypeA(valueA=A, valueB=I am value B)
It's hard to see how you could develop a feature like readerForUpdating() which would know which fields to adjust in a deep object graph. If the incoming object's object's field had a value, should that indicate an overwrite? but not if they come as nulls?
If I had to do this way myself, I might approach it this way:
read the incoming JSON into a traditional JSONObject then walk the keys and set the properties on the target object
For setting properties you could use old school Apache Commons BeanUtils tool, but I am not sure that that is maintained and it has had some vulnerabilities issues, so you could look at Spring's BeanWrapperImpl
I have a org.json.JSONObject like below:
{"entries":
[{"entry":{"createdAt":"2020-06-25T18:10:22.571+0000","isFolder":false,"isFile":true,"createdByUser":{"displayName":"Administrator","id":"admin"},"modifiedAt":"2020-09-28T15:42:50.253+0000","modifiedByUser":{"displayName":"Administrator","id":"admin"},"name":"1000024_Resume 1-K_User1 (2020-1601307769393).doc","id":"a9aa23ac-3cca-4fd7-9f82-ec31c2b969f0","nodeType":"hr:HR_Type","content":{"sizeInBytes":48128,"mimeTypeName":"Microsoft Word","mimeType":"application/msword","encoding":"UTF-8"},"parentId":"7db2d13f-db92-4401-aff1-cecddd78db45"}},
{"entry":{"createdAt":"2020-06-25T18:10:23.014+0000","isFolder":false,"isFile":true,"createdByUser":{"displayName":"Administrator","id":"admin"},"modifiedAt":"2020-07-10T20:40:33.123+0000","modifiedByUser":{"displayName":"Sarah Campbell","id":"SACAMPBELL"},"name":"Test.DOC","id":"29cfee8d-5614-4c81-9bfa-581334cc39e9","nodeType":"hr:Test_Type","content":{"sizeInBytes":35328,"mimeTypeName":"Microsoft Word","mimeType":"application/msword","encoding":"UTF-8"},"parentId":"79d3b939-b7e9-4bed-be67-428eb5da0f16"}},
{"entry":{"createdAt":"2020-07-10T15:06:06.252+0000","isFolder":false,"isFile":true,"createdByUser":{"displayName":"Test Display","id":"CN158931"},"modifiedAt":"2020-09-28T15:39:40.349+0000","modifiedByUser":{"displayName":"Administrator","id":"admin"},"name":"1000536_Test Display September 2014.doc","id":"9eea5068-48dc-4e1f-9a19-e7d9749ba3db","nodeType":"hr:Test_Type","content":{"sizeInBytes":58243,"mimeTypeName":"Microsoft Word","mimeType":"application/msword","encoding":"UTF-8"},"parentId":"79d3b939-b7e9-4bed-be67-428eb5da0f16"}},
{"entry":{"createdAt":"2020-07-10T21:09:50.889+0000","isFolder":false,"isFile":true,"createdByUser":{"displayName":"Test Display-Name","id":"CN103107"},"modifiedAt":"2020-07-10T21:11:26.528+0000","modifiedByUser":{"displayName":"Some-CGT12","id":"CN103107"},"name":"Test Display123.jpg","id":"df7c76a1-67b9-4673-8fb7-1a2470d42c1d","nodeType":"hr:Test_Type","content":{"sizeInBytes":237560,"mimeTypeName":"JPEG Image","mimeType":"image/jpeg","encoding":"UTF-8"},"parentId":"7db2d13f-db92-4401-aff1-cecddd78db45"}},
{"entry":{"createdAt":"2020-07-10T21:09:51.706+0000","isFolder":false,"isFile":true,"createdByUser":{"displayName":"Test Display-Name","id":"CN103107"},"modifiedAt":"2020-07-10T21:09:51.706+0000","modifiedByUser":{"displayName":"Some-TEst2","id":"CN103107"},"name":"batman.jpg","id":"88ac8b96-5965-4668-9e94-2b2e3509e0f8","nodeType":"hr:HR_Type","content":{"sizeInBytes":5588,"mimeTypeName":"JPEG Image","mimeType":"image/jpeg","encoding":"UTF-8"},"parentId":"79d3b939-b7e9-4bed-be67-428eb5da0f16"}}]
,"pagination":{"maxItems":100,"hasMoreItems":false,"totalItems":5,"count":5,"skipCount":0}}
How do I get the value for all "id"s (a9aa23ac-3cca-4fd7-9f82-ec31c2b969f0, 29cfee8d-5614-4c81-9bfa-581334cc39e9, 9eea5068-48dc-4e1f-9a19-e7d9749ba3db, df7c76a1-67b9-4673-8fb7-1a2470d42c1d, 88ac8b96-5965-4668-9e94-2b2e3509e0f8) from above without using for loops. That means I dont want to iterate through all the objects and then get "id" value.
I am trying to do something like:
org.json.JSONObject myJSONObject = new org.json.JSONObject(response.getBody()).getJSONObject("entries").getJSONObject("entry").getString("id");
If possible, I am looking for one-liner using Java 1.6/1.7/1.8 for above.
I am not sure if there is a single liner solution, but it becomes very easy using JSON PATH library
https://mvnrepository.com/artifact/com.jayway.jsonpath/json-path
Configuration cf = Configuration.builder().options(Option.SUPPRESS_EXCEPTIONS).build();
DocumentContext ctx = JsonPath.using(cf).parse(jsonStr);
List<String> ids = ctx.read("$.entries[*].entry.id");
I need to update an index document for an elasticsearch table and this is the code I have implemented. But it is not working, what's wrong and how should I implement this?
My code.
Map<String, Object> matching_result;
for (SearchHit hit : response_text.getHits()) {
matching_result = hit.getSource();
String flag_value = matching_result.get("flag").toString();
matching_result.put("flag", true);
}
String indexString = JSONConverter.toJsonString(matching_result);
IndexResponse response = client.prepareIndex("index_name", "data").setSource(indexString).execute().actionGet();
boolean created = response.isCreated();
System.out.println("created or updated--------------------->" + created);
System.out.println("flag value==========" + matching_result.get("flag"));
return actual_theme;
(JSONConverter.toJsonString) is our library class for converting to json string.
What is wrong with this query?
Instead of updating the existing document it is creating a new one. I want to change the existing one.
Based on your example code, it looks like by "update" you mean you are trying to replace the entire document. In order to do this, you must specify the id of the document you wish to update.
Using the Java API, in addition to calling setSource on the IndexRequestBuilder, you would also need to supply the id by calling setId. For example:
IndexResponse response = client.prepareIndex("index_name", "data")
.setSource(indexString)
.setId(123) <----- supply the ID of the document you want to replace
.execute()
.actionGet();
Otherwise, just so you know, in ES you have the option to do a partial update. That is, only update certain fields in the document. This can be done with a script or by providing a partial document. Have a look at the documentation for the Update API.
In either case, you need to provide ES with the ID for the document you wish to modify.
Since my very first days of Java + JSON I tried to extract just some certain parts of a JSON.
But no matter if which of the libraries I used:
Gson
json-simple
javax.json
it never was possible to make it quick and comfortable. Mostly for easy task or even prototyping. It already cost me many hours of different approaches.
Going trough the hierarchy of an JSON
Object jsonObject = gson.fromJson(output, Object.class);
JsonElement jsonTree = gson.toJsonTree(jsonObject);
JsonArray commitList = jsonTree.getAsJsonArray();
JsonElement firstElement = commitList.get(0);
JsonObject firstElementObj = firstElement.getAsJsonObject();
System.out.println(firstElementObj.get("sha"));
JsonElement fileList = firstElementObj.get("files");
This is dirty code for a reason. It shows how many early approaches looks like and how many people cannot achieve it to do it better early.
Deserializing JSON to a Java Object
Your have to analyse the complete JSON to create an complete Java-Object representation just to get access to some single memebers of it. This is a way I never wanted to do for prototyping
JSON is an easy format. But using libraries like that is quite difficult and often an problem for beginner. I've found several different answers via Google and even StackOverflow. But most were quite big larged which required to create a own specific class for the whole JSON-Object.
What is the best approach to make it more beginner-friendly?
or
What is the best beginner-friendly approach?
Using Jackson (which you tagged), you can use JsonPointer expressions to navigate through a tree object:
ObjectMapper mapper = new ObjectMapper();
JsonNode tree = mapper
.readTree("[ { \"sha\": \"foo\", \"files\": [ { \"sha\": \"bar\" }, { \"sha\": \"quux\" } ] } ]");
System.out.println(tree.at("/0/sha").asText());
for (JsonNode file : tree.at("/0/files")) {
System.out.println(file.get("sha").asText());
}
You could also use the ObjectMapper to convert just parts of a tree to your model objects, if you want to start using that:
for (JsonNode fileNode : tree.at("/0/files")) {
FileInfo fileInfo = mapper.convertValue(fileNode, FileInfo.class);
System.out.println(fileInfo.sha);
}
If your target class (FileInfo) specifies to ignore unknown properties (annotate target class with #JsonIgnoreProperties(ignoreUnknown = true) or disable DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES on the mapper), then you can simply declare the properties you are interested in.
"Best" is whatever works to get you going.
Generate Plain Old Java Objects from JSON or JSON-Schema
One little helper I found via my research was an Online-Tool like
http://www.jsonschema2pojo.org/
This is a little help, when you know about that. But the negative side I mentioned at point 2 is still there.
You can use JsonSurfer to selectively extract value or object from big json with streaming JsonPath processor.
JsonSurfer jsonSurfer = JsonSurfer.gson();
System.out.println(jsonSurfer.collectOne(json, "$[0].sha"));
System.out.println(jsonSurfer.collectOne(json, "$[0].files"));
I'm developing a Java application using MongoDB and Java-driver.
I need to parse a BasicDBObject into an own object of my code, and I don't know if there is a way to develop automatically.
Is it possible to parse from BasicDBObject to JSON String? Then, I could parse from JSON String to my own Object, for example with GSON library. Something like
BasicDBObject object;
String myJSONString = object.toString();
Gson gson = new Gson();
MyOwnObject myObject = gson.fromJson(myJSONString, MyOwnObject.class);
And I don't want to add complex to my code, also I don't to add more extern libraries. I don't want to add Gson library or other.
Any ideas?? Is it possible to do this without external libraries??
Thanks!!
You could have taken a look at the API: Just call object#toString() (http://api.mongodb.org/java/2.0/com/mongodb/BasicDBObject.html#toString()).
You could either use Groovy with gmongo library for that, there you have lots of handy tools for such casting.
If the language change is not an option for you, write your own reflection-based mapper. If you POJO is simple enough, the mapper shall be pretty simple.
This is the correct answer to my question
From http://docs.mongodb.org/ecosystem/tutorial/use-java-dbobject-to-perform-saves/
For example, suppose one had a class called Tweet that they wanted to save:
public class Tweet implements DBObject {
/* ... */
}
Then you can say:
Tweet myTweet = new Tweet();
myTweet.put("user", userId);
myTweet.put("message", msg);
myTweet.put("date", new Date());
collection.insert(myTweet);
When a document is retrieved from the database, it is automatically converted to a DBObject. To convert it to an instance of your class, use DBCollection.setObjectClass():
collection.setObjectClass(Tweet.class);
Tweet myTweet = (Tweet)collection.findOne();
If for some reason you wanted to change the message you can simply take that tweet and save it back after updating the field.
Tweet myTweet = (Tweet)collection.findOne();
myTweet.put("message", newMsg);
collection.save(myTweet);