JSONata: How to substitute . with _ in key names? - jsonata

Update: editing question to provide a multi-level object example:
{
"An.Example": "Dots.are.okay.in.values",
"Product": {
"Product.ID.Number": 858383 ,
"Product.Name": "Bowler Hat",
"Unit.Cost": 12
}
}
Exerciser: https://try.jsonata.org/ZdaDTJFMA
[original question]
Hello JSONata newbie checking in again. Given this source which I'm keeping very simple for the purposes of this example...
{
"Product.Name": "Bowler Hat",
"Product.ID.Number": 858383
}
How can I transform any "." in key names to "_"? The key names will not be known in advance so I can't simply hard code. Desired result:
{
"Product_Name": "Bowler Hat",
"Product_ID_Number": 858383
}
I took a look at the example shown here -- https://try.jsonata.org/ry9G5Xr3H -- which was in response to this question -- Find fields which contains a text and replace it with another text -- but cannot come up w/ the proper adjustments to address my need.
Sample JSON and non-working code here -- https://try.jsonata.org/uOwYJOLto
Thank you in advance for any assistance or suggestions.

$each(function($v, $k) {
{ $replace($k, '.', '_'): $v}
}) ~> $merge()
See https://try.jsonata.org/uZgm69Bfy
If you want to traverse a hierarchy of objects, then you'll need write a recursive function:
(
$dotsToUnderscores := $each(?, function($v, $k) {
{ $replace($k, '.', '_'): $type($v) = 'object' ? $dotsToUnderscores($v) : $v}
}) ~> $merge;
$dotsToUnderscores($)
)
See https://try.jsonata.org/WsfsFGhjE or https://try.jsonata.org/r2GrHYYou

Related

How to combine Aggregate function with update

I could not find a way to translate the fowling MongoDb command into C#
db.Queue.aggregate(
[
{ $match: { "Processed": false } },
{ $sort: { "LastTimeChanged": 1  } },
{ $limit: 1 },
{ $set: { "WorkerName": "WORKER_NAME", "Processed": true }  },
{ "$merge": "Queue"  }])
The issues that I fund was with the $set and $merge command
$set -> in the MongoDb.Driver for .NET, associated with the Aggregate command I could not find any command that look like the $set
$merge -> the merge command examples are exclusive for merging collections and in this case, I could not find a way to use the Merge method in the API.
Any one can throw light here!??
thanks
Paulo Aboim Pinto
I found a way to execute the command using the MongoDb.Driver but I thing there should be a better and fluent way of doing it
var filter = Builders<QueueCollection>.Filter.And
(
Builders<QueueCollection>.Filter.Eq(x => x.WorkerName, string.Empty),
Builders<QueueCollection>.Filter.Eq(x => x.Processed, false)
);
var sortOptions = Builders<QueueCollection>
.Sort.Ascending("LastTimeChanged");
this.queueCollection.Aggregate()
.Match(filter)
.Sort(sortOptions)
.Limit(1)
.AppendStage<QueueCollection>("{ $set: { 'WorkerName': 'WORKER_NAME' } }")
.AppendStage<QueueCollection>("{ $merge: 'Queue' }")
.ToList();
This works for now, but I would like to want still to know:
How do I replace the $set in the Aggregate pipeline
How do I write a proper $merge command.
thanks in advance for any answer
Paulo Aboim Pinto

I want to update values ​in an array in an array while using MongoTemplate

First, I will show the state stored in mongodb.
As you can see, it is a structure with a list called replies in a list called comments. And inside replies there is an array called likes.
comments : [
Object1 : {
replies : [
likes : [
0 : {},
1 : {}
]
]
},
Object2 : {
replies : [
likes : [
0 : {},
1 : {}
]
]
}
]
What I want to do here is to insert/subtract a value only from the likes array inside a specific replies structure. I'm currently using Spring boot and have tried the following:
Query query = new Query();
Criteria criteria = Criteria.where("_id").is(new ObjectId(postId))
.andOperator(Criteria.where("comments")
.elemMatch(Criteria.where("_id").is(new ObjectId(commentId))
.andOperator(Criteria.where("replies")
.elemMatch(Criteria.where("_id").is(new ObjectId(replyId)))
)
)
);
query.addCriteria(criteria);
Update update = new Update();
if (state) {
// remove user id
update.pull("comments.$[].replies.$.likes", new ObjectId(userId));
} else {
// add user id
update.push("comments.$[].replies.$.likes").value(new ObjectId(userId));
}
mongoTemplate.updateFirst(query, update, MyEntity.class);
It is an operation to add or remove userId according to boolean state. As a result of the attempt, up to a specific comment is found, but userId is unconditionally entered in the first likes list of the replies list inside the comment. What I want is to get into the likes list inside a specific reply. Am I using the wrong parameter in update.push()? I would appreciate it if you could tell me how to solve it.
Not a direct answer to your question as I'm not experienced with spring's criteria builder, but here's how you would do it in mongo directly, which might help you to figure it out:
You could define arrayfilters allowing you to keep track of the corresponding indices of each comments and replies. You can then use those indices to push a new object at the exact matching indices:
db.collection.update({
_id: "<postId>"
},
{
$push: {
"comments.$[comments].replies.$[replies].likes": {
_id: "newlyInsertedLikeId"
}
}
},
{
arrayFilters: [
{
"comments._id": "<commentId>"
},
{
"replies._id": "<replyId>"
}
]
})
Here's an example on mongoplayground: https://mongoplayground.net/p/eNdDXXlyi2X

JSONata transformation script for taking source as it is with some override/modifications

I have JSON, for example:
{
"PDMSReferenceNumber": "11340",
"OntologyClass": "rdl:P101003917",
"TopTag": "DEEP1",
"ServiceDescription2": "Main manual",
"SystemVoltagePrimaryWinding": "",
"ClearOpeningHeight": "true"
}
Is it possible to create JSONata script like this:
{
"*": *,
"MainTag": TopTag
}
The result should be
{
"PDMSReferenceNumber": "11340",
"OntologyClass": "rdl:P101003917",
"ServiceDescription2": "Main manual",
"SystemVoltagePrimaryWinding": "",
"ClearOpeningHeight": "true",
"MainTag": "DEEP1"
}
So I want to take the source JSON is it is and make some override and modifications.
Thank you!
You could try using the Transform Function - https://docs.jsonata.org/other-operators#-------transform
So this basic "copys" TopTag to MainTag, and adds to object and then deletes TopTag
$ ~> |$|{'MainTag': TopTag}, ['TopTag']|
Here you go to show it:
https://try.jsonata.org/YqpO6oUk9
Exactly what JSONata does for you "transform" JSON.
You can simply do this:
$.{
"MainTag": TopTag,
"PDMSReferenceNumber": PDMSReferenceNumber,
"OntologyClass": OntologyClass,
"ServiceDescription2": ServiceDescription2,
"SystemVoltagePrimaryWinding": SystemVoltagePrimaryWinding,
"ClearOpeningHeight": ClearOpeningHeight
}
So the left part is the "key" for your new object, and the right is the "Key" from your source JSON (hence we have "MainTag": TopTag)

Filter by Date with Spring Data MongoTemplate

I would like to filter my filedA's array by dates, with a mongo query it looks like that :
{
$project: {
user: "$$ROOT",
fieldA: {
$filter: {
input: "$fieldA",
as: "a",
cond: {
$and: [
{$lt: ["$$a.constraint", new Date()]},
{$gt: ["$$a.constraint", new Date()]}
]
}
}
}
}
},
The query works but I have trouble when I tried to do it with spring :
project()
.and("$$ROOT").as("user")
.and(
filter("$fieldA")
.as("a")
.by(
and(
ComparisonOperators.Lte.valueOf("a.constraint")
.lessThanEqualTo(dateEnd),
ComparisonOperators.Gte.valueOf("a.constraint")
.greaterThanEqualTo(dateStart)
)
)).as("fieldA"),
I think this is not the right way to make dates comparations but I don't know how to do it properly. Could help me figure out what I'm doing wrong?
try wrapping your dateStart and dateEnd variables in ConvertOperators.ToDate.toDate.
The ComparisonOperators methods (in this case lessThanEqualTo and greaterThanEqualTo) accept either String or AggregationExpression.
By using the ConvertOperators.ToDate.toDate method, you ensure that the passed argument is AggregationExpression and the value is correctly formatted for comparing.
Result would look like this:
import static org.springframework.data.mongodb.core.aggregation.ConvertOperators.ToDate.toDate
...
project()
.and("$$ROOT").as("user")
.and(
filter("$fieldA")
.as("a")
.by(
and(
ComparisonOperators.Lte.valueOf("a.constraint")
.lessThanEqualTo(toDate(dateEnd)),
ComparisonOperators.Gte.valueOf("a.constraint")
.greaterThanEqualTo(toDate(dateStart))
)
)).as("fieldA"),

Create or append to an array in a rethinkdb document

How should we append an item to an array if the array exists, or create an array and insert to it.
I tried the merge command but that doesn't allow merging arrays, only replacing them.
r.db('testdb').table('users').get('27e55a4a-a6f8-4ec9-bd02-f55f206700ff').merge({ 'hobbies':['Reading'] })
I further tried passing a function but doesnt seem to work:
r.db('testdb').table('users').get('27e55a4a-a6f8-4ec9-bd02-f55f206700ff').merge(function(user) {
return r.branch(user('hobbies').eq(null),
{ 'hobbies' : ['Reading'] }
user('hobbies').append('Reading'))
});
Consider the below doc structure:
{
"email": email.123#gmail.com, »
"id": "27e55a4a-a6f8-4ec9-bd02-f55f206700ff" ,
"image": https://lh4.googleusercontent.com/-O4ZXcLRpkHE/AAArAAAAAAAI/AdAAAAAAALMM/Fq968TTkd88Y/photo.jpg?sz=50, »
"name": "John Doe"
}
If I would like to add hobbies as an array how should I do it. The query has to work both if the hobby array exists or not.
The most idiomatic way would be .update(function(user) { return {hobbies: user('hobbies').default([]).append('reading')}; })
Finally I have figured out myself.
r.db('testdb').table('users')
.get("27e55a4a-a6f8-4ec9-bd02-f55f206700ff")
.update(function(user){
return r.branch(user.hasFields('hobbies'),
{ hobbies: user('hobbies').append('reading')},
{ hobbies : ['reading']}
)})

Resources