How to update many records with `dateAdd` in Java - java

With a collection of
[
{
"user": "abc",
"seconds": 1111,
"time": ISODate("2020-05-05T00:00:00Z")
},
{
"user": "abc",
"seconds": 2222,
"time": ISODate("2020-05-05T00:00:00Z")
}
]
I need to have another field, which adds the seconds to the time of individual record.
This seems to be possible with dateAdd which is added in version 5 (the database is version 5).
However, I am not able to achieve that with MongoDB Java driver 4.6.0.
var alerts = db.getCollection("alerts");
alerts.updateMany(eq("user", user),
set(new Field<>("time2",
new Document("$dateAdd",
new Document("startDate", "$time")
.append("unit", "second")
.append("amount", "$seconds"))
)));

Since mongoDB version 4.0, before $dateAdd of version 5.0, you can use $toDate:
db.collection.update({},
[
{
$set: {
time: {
$toDate: {
$add: [
{$toLong: "$time"},
{$multiply: ["$seconds", 1000]}
]
}
}
}
}
],
{multi: true})
See how it works on the playground example

Related

Updating JSON Object in Mongodb using spring boot

Suppose I have a JSON Object which needs to be updated in Mongodb like
{
"_id": 12345,
"Attribute": { "Property1": "X", "Property2": true, "Property3": 123 }
}
Suppose I have a record in mongoDb
{
"_id": 12345,
"Attribute1": "abc",
"Attribute2": "xyz",
"Attribute": { "Property4": "X", "Property2": false, "Property3": 456 }
}
The result should update Attribute JSON while updating only the fields that are changed and keeping rest of the values intact.
Resultant record in db should be like this
{
"_id": 12345,
"Attribute1": "abc",
"Attribute2": "xyz",
"Attribute": { "Property4": "X", "Property1": "X", "Property2": true, "Property3": 123 }
}
I really don't know how to achieve this in single Pass in Mongodb using JAVA spring boot. Can Anyone please help? Any help is appreciated.
You can use Update class from org.springframework.data.mongodb.core.query package. You can write a code snippet like below.
Update updateAttribute = new Update();
updateAttribute.push("Attribute", "Your Value");
mongoOperations.updateFirst(new Query(Criteria.where("id").is(12345)), updateAttribute, "yourCollection");
Also, you need to inject constructor for MongoOperations from org.springframework.data.mongodb.core package.
You can do it in 2 ways,
For Mongo version 4.4+ you can use pipelined updates, this allows the use of aggregation operators in the update body, specifically we'll want to use $mergeObjects, like so:
db.collection.update(
{
_id: 12345
},
[
{
$set: {
Attribute: {
$mergeObjects: [
"$Attribute",
{
"Property1": "X",
"Property2": true,
"Property3": 123
}
]
}
}
}
])
Mongo Playground
For lesser Mongo versions you'll have to construct the update body in code, here is a javascript example ( might be slightly more annoying in spring )
const input = {
'_id': 12345,
'Attribute': { 'Property1': 'X', 'Property2': true, 'Property3': 123 },
};
const updateBody = {};
Object.keys(input.Attribute).forEach((key) => {
const updateKey = `Attribute.${key}`;
updateBody[updateKey] = input.Attribute[key];
});
db.collection.updateOne({ _id: 12345 }, { $set: updateBody });
By using the dot notation in the update body we ensure we don't overwrite existing fields in the Attribute.
I achieved this using this query.
db.collection.update(
{
_id: 12345
},
{
$set: {
"Property1": "X",
"Property2": true,
"Property3": 123
}
},
{upsert: true}
)

How to Store Complex Json in Redis using ReJson

I need to store java object (may be json formatted) in Redis. I was searching over internet and found ReJson module.
{
"site": "sddd",
"pConfig" : {
"floatpoint" : "http://10.32.3.36:8003",
"user" : "root",
"password" : "xxx"
},
"Config": {
"initInSecs": 0,
"checkInSecs": 29
},
"refC": {
"initSecs": 0,
"InSecs": 59,
"InSecsOnDown": 15,
"InMillis" : 5000,
"endPoints": [
{
"ip": "10.32.17.66",
"port": "22"
},
{
"ip": "10.32.17.66",
"port": "21"
}
]
},
"syncWConfig": {
"initDelayInSecs": 0
}
}
Can you please help how to store this Json using ReJson. I also want to retrieved elements and its values. Can you help with small code snippet.
You should check JRedisJSON java client
https://github.com/RedisJSON/JRedisJSON
As for search and secondary index support it should be available soon for RedisJSON see https://github.com/RedisJSON/RedisJSON2

Completion Suggester in elasticsearch in mutifield

I'm using elasticsearch for the first time. I'm trying to use completion suggester in multi-field key, although I don't see any error but I don't get the response.
Mapping creation:
PUT /products5/
{
"mappings":{
"products" : {
"properties" : {
"name" : {
"type":"text",
"fields":{
"text":{
"type":"keyword"
},
"suggest":{
"type" : "completion"
}
}
}
}
}
}
}
Indexing:
PUT /products5/product/1
{
"name": "Apple iphone 5"
}
PUT /products5/product/2
{
"name": "iphone 4 16GB"
}
PUT /products5/product/3
{
"name": "iphone 3 SS 16GB black"
}
PUT /products5/product/4
{
"name": "Apple iphone 4 S 16 GB white"
}
PUT /products5/product/5
{
"name": "Apple iphone case"
}
Query:
POST /products5/product/_search
{
"suggest":{
"my-suggestion":{
"prefix":"i",
"completion":{
"field":"name.suggest"
}
}
}
}
Output:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 0,
"max_score": 0,
"hits": []
},
"suggest": {
"my-suggestion": [
{
"text": "i",
"offset": 0,
"length": 1,
"options": []
}
]
}
}
Please guide me what is the mistake, I tried every possible options.
From the first perspective this looks accurate. Probably the reason why you don't have correct response is that you added documents in the index before you created mapping in the index. And documents are not indexed according to the mapping you specified
I have found an issue in your mapping name. There is an inconsistency between name of the mapping and value which you specifies in the url when you're creating new documents. You create a mapping in the index with the name products. And when you add new documents you're specifying product as a name of the mapping of your index and it doesn't end with s. You have a typo.

Union searches through elasticsearch and spring

Currently we are searching through elastic with multiple requests.
What I want is that, if, for instance, you have an index of fruits, with data "calories", "name" and "family", I want top 3 (calory based) fruits with family "a", top 3 with "b" and top 3 with "c".
Currently I would search 3 times, making a query look like:
{
"sort": [ {"calories": "desc"} ],
"query": {
"bool" : {
"must": [
{"term": { "family": "a" }} // second time "b", third time "c"...
]
}
},
"from": 0,
"size": 3
}
Using QueryBuilders.boolQuery().must(QueryBuilders.termQuery("family", "a"));
(Being that the query above would be in a loop, so second time it's "b", third time "c")
My question is if I can somehow do a functionality similar to UNION from SQL? Joining 3 results with family "a", 3 with family "b" and 3 with family "c". Also how would this be done in Java (Spring Boot) would be very helpful!
Thanks! If the description/explanation isn't good, please tell me, I'll try to elaborate.
You could perform a multi-search and do the UNION in Java (this is the better way so you can rank results easily).
Or, use a bool should query to do OR clauses.
"bool" : {
"should": [
{"term": { "family": "a" }},
{"term": { "family": "b" }},
{"term": { "family": "c" }}
]
}
BUT it's hard to control how many results by family.
So another solution is to use a terms aggregation + top_hits:
(https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-top-hits-aggregation.html)
{
"query": {
"match_all": {}
},
"aggs": {
"family": {
"terms": {
"field": "family"
},
"aggs": {
"top_sales_hits": {
"top_hits": {
"sort": [
{
"date": {
"order": "desc"
}
}
],
"_source": {
"includes": [
"date",
"price"
]
},
"size": 10
}
}
}
}
}
}
Note: this is just an example, not a working solution.

ElasticSearch: Update multi fields in Script Java Plugin

I saw this snippet where update_by_query can update source directly
POST twitter/_update_by_query
{
"script": {
"inline": "ctx._source.likes++",
"lang": "painless"
},
"query": {
"term": {
"user": "kimchy"
}
}
}
instead of using painless, I wrote a native script plugin in Java because of my complex business logic.
{
"subtotal": 1000,
"markup": 2,
"total": 2000,
"items": [
{
"subtotal": 100,
"markup": 2,
"total": 200
},
{
"subtotal": 500,
"markup": 2,
"total": 1000
}
]
}
User can set markup value in the application. If user change markup to 3, I want to update markup and total field including the ones in nested object. (NOTE: I can't use painless because in my case, the logic is more complicated than just multiplies those fields. That's why I use Java)
// my plugin code
public Object run() {
// change field value of "markup"
// change field value of "total"
return true;
}
My Code is almost similar with https://github.com/imotov/elasticsearch-native-script-example/blob/master/src/main/java/org/elasticsearch/examples/nativescript/script/TFIDFScoreScript.java
I was trying with source().put("markup", 3) but I kept getting NullPointerException
ElasticSearch Version: 5.0.0
Thank you

Categories