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']}
)})
Related
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
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
I have a table in rethinkdb where each row has following structure -
{
'name':'clustername',
'services':[
{
'name':'service1'
},
{
'name':'service2'
}
]
}
I am running a query to filter service2 object like this
r.table('clusters').filter({"name": "clustername"})
.pluck('services').filter((service) => {
return service("name").match('service2')
})
But this is not returning anything: No results were returned for this query
Can anyone tell why this is happening?
pluck returns sequence, so this query:
r.table('clusters').filter({"name": "clustername"}).pluck('services')
will return:
{
"services": [
{
"name": "service1"
} ,
{
"name": "service2"
}
]
}
You need get services field from it, it will return array with services field of items found by filter.
And after that you need to use your second filter on each item by using map.
So, correct query:
r.table('clusters').filter({"name": "clustername"}).pluck('services')("services").map(item => {
return item.filter(service => {
return service("name").match("service2");
});
})
I am hitting the following problem: Suppose that I have the following structure:
{
"id": 1,
"data": {
"arr": [{"text":"item1"}]
}
}
And the following query:
r.db('test').table('test').get(1).update(function (item) {
return {
data: {
arr: item('data')('arr').map(function (row) {
return r.branch(
row('text').eq('item1'),
row.merge({updated:true}),
row
)
})
}
}
})
I am listening for changes in this specific array only, and when the item is updated both create and delete events are emitted. I really need to receive an update event, e.g. old_val is not null and new_val is not null.
Thanks in advance guys
After all, I decided to drop the embedded array and use table joins, this avoids all possible hacks.
You can use something like this
r.db('test').table('test')('data')('arr').changes()
.filter(function(doc) {
return doc('new_val').ne(null).and(doc('old_val').ne(null))
})
I'll only show update to array. If you need to get access to other document field, try this:
r.db('test').table('test').changes()
.filter(function(doc) {
return doc('new_val')('data')('arr').ne(null).and(doc('old_val')('data')('arr').ne(null))
})
I am developing a web app using Codeigniter and MongoDB.
I am trying to get the map reduce to work.
I got a file document with the below structure. I would like to do a map reduce to
check how many times each tag is being used and output it to the collection files.tags.
{
"_id": {
"$id": "4f26f21f09ab66c1030d0000e"
},
"basic": {
"name": "The filename"
},
"tags": [
"lorry",
"house",
"car",
"bicycle"
],
"updated_at": "2012-02-09 11:08:03"
}
I tried this map reduce command but it does not count each individual tag:
$map = new MongoCode ("function() {
emit({tags: this.tags}, {count: 1});
}");
$reduce = new MongoCode ("function( key , values ) {
var count = 0;
values.forEach(function(v) {
count += v['count'];
});
return {count: count};
}");
$this->mongo_db->command (array (
"mapreduce" => "files",
"map" => $map,
"reduce" => $reduce,
"out" => "files.tags"
)
);
Change your Map function to:
function map(){
if(!this.tags) return;
this.tags.forEach(function(tag){
emit(tag, {count: 1});
});
}
Yea, this map/reduce simply calculate total count of tags.
In mongodb cookbook there is example you are looking for.
You have to emit each tag instead of entire collection of tags:
map = function() {
if (!this.tags) {
return;
}
for (index in this.tags) {
emit(this.tags[index], 1);
}
}
You'll need to call emit once for each tag in the input documents.
MongoDB documentation for example says:
A map function calls emit(key,value) any
number of times to feed data to the reducer. In most cases you will
emit once per input document, but in some cases such as counting tags,
a given document may have one, many, or even zero tags.