rethinkdb - hasFields to find all documents with multiple multiple missing conditions - rethinkdb

I found an answer for finding all documents in a table with missing fields in this SO thread RethinkDB - Find documents with missing field, however I want to filter according to a missing field AND a certain value in a different field.
I want to return all documents that are missing field email and whose isCurrent: value is 1. So, I want to return all current clients who are missing the email field, so that I can add the field.
The documentation on rethink's site does not cover this case.
Here's my best attempt:
r.db('client').table('basic_info').filter(function (row) {
return row.hasFields({email: true }).not(),
/*no idea how to add another criteria here (such as .filter({isCurrent:1})*/
}).filter

Actually, you can do it in one filter. And, also, it will be faster than your current solution:
r.db('client').table('basic_info').filter(function (row) {
return row.hasFields({email: true }).not()
.and(row.hasFields({isCurrent: true }))
.and(row("isCurrent").eq(1));
})
or:
r.db('client').table('basic_info').filter(function (row) {
return row.hasFields({email: true }).not()
.and(row("isCurrent").default(0).eq(1));
})

I just realized I can chain multiple .filter commands.
Here's what worked for me:
r.db('client').table('basic_info').filter(function (row) {
return row.hasFields({email: true }).not()
}).filter({isCurrent: 1}).;
My next quest: put all of these into an array and then feed the email addresses in batch

Related

How to update item conditionally with branch in RethinkDB

I am trying to do simple upsert to the array field based on branch condition. However branch does not accept a reql expression as argument and I get error Expected type SELECTION but found DATUM.
This is probably some obvious thing I've missed, however I can't find any working example anywhere.
Sample source:
var userId = 'userId';
var itemId = 'itemId';
r.db('db').table('items').get(itemId).do(function(item) {
return item('elements').default([]).contains(function (element) {
return element('userId').eq(userId);
}).branch(
r.expr("Element already exist"),
//Error: Expected type SELECTION but found DATUM
item.update({
elements: item('elements').default([]).append({
userId: 'userId'
})
})
)
})
The problem here is that item is a datum, not a selection. This happens because you used r.do. The variable doesn't retain information about where the object originally came from.
A solution that might seem to work would be to write a new r.db('db').table('items').get(itemId) expression. The problem with that option is the behavior isn't atomic -- two different queries might append the same element to the 'elements' array. Instead you should write your query in the form r.db('db').table('items').get(itemId).update(function(item) { return <something>;) so that the update gets applied atomically.

Proper Upsert (Atomic Update Counter Field or Insert Document) with RethinkDB

After looking at some SO questions and issues on RethinkDB github, I failed to come to a clear conclusion if atomic Upsert is possible?
Essentially I would like to perform the same operation as ZINCRBY using Redis.
If member does not exist in the sorted set, it is added with increment
as its score (as if its previous score was 0.0). If key does not
exist, a new sorted set with the specified member as its sole member
is created.
The current implementation appears to differ from almost all databases that I have used. With the data being replaced or inserted not updated. This is a simple use case, like update the last visit, update the number of clicks, update a product quantity. So I must be missing something very obvious, because I cannot see a simple way to do this.
Yes, it is possible. After get on the key, perform an atomic replace. Something like this might work:
function set_or_increment_score(player, points){
return r.table('scores').get(player).replace(
row =>
{ id: player,
score: r.branch(
row.eq(null),
points,
row('score').add(points))
});
}
It has the following behaviour:
> set_or_increment_score("alice", 1).run(conn)
{ inserted: 1 }
> set_or_increment_score("alice", 2).run(conn)
{ replaced: 1 }
It works because get returns null when the document doesn't exist, and a replace on a non-existing document tuns into an insert. See the documentation for replace
So I end up using the following code to go around the no Update issue.
r.db("test").table("t").insert(
{id:"A", type:"player", species:"warrior", score:0, xp:0, armor:0},
{conflict: function(id, oldDoc, newDoc) {
return newDoc.merge(oldDoc).merge(
{armor: oldDoc("armor").add(1)});
}
}
)
Do you think this is more readable/elegant or do you see any issues with the code compared to your sample?

How to get a list of all keys across all documents in a RethinkDB table?

I have a dynamically populated table in which documents can have different keys that are not known in advance:
Document 1
{
'attribute1': 'foo',
'attribute2': 'bar'
}
Document 2
{
'attribute1': 'foo',
'attribute3': 'baz'
}
How can I get a list of all attributes present in all documents?
attribute1
attribute2
attribute3
I've tried grouping by keys() but I get a list of the possible attribute combinations, not the individual keys.
While this isn't fast enough if you have a lot of document, it will eventually finishes and won't consume lots of memory:
r.table('table')
.map(r.row.keys())
.reduce(function(left, right) {
return left.setUnion(right)
})
It will be slow, but you can write something like table.concatMap(function(row) { return row.keys(); }).distinct().
I'm not sure there's a solution that is more efficient than O(n) (?) unless you update some custom meta-data by each data update, but anyways, I guess I'd go
table.reduce(function(left, right) {
return left.merge(right)
}).keys()
You can simply create a secondary index of type multi:
r.table('foo').indexCreate('all_keys', function(d){
return d.without('id').keys()
}, {multi: true})
And to get all the keys, just run:
r.table('foo').distinct({index: 'all_keys'})
Voila ;-)

Removing a field and updating another field in a document

Is it possible to remove a field from a document and update another field in the same document in one query?
Afaik, to remove field, you have to use a replace query, like so:
r.db("db").table("table").get("some-id").replace(r.row.without("field-to-remove"))
And to update:
r.db("db").table("table").get("some-id").update({ "field-to-update": "new-value" })
But chaining these two together doesn't work. I get a "RqlRuntimeError: Expected type SELECTION but found DATUM" error when running the following query (the order of the replace/update doesn't matter):
r.db("db").table("table").get("some-id").replace(r.row.without("field-to-remove")).update({ "field-to-update": "new-value" })
Try:
r.db('db').table('table').get('id').update({
"field-to-remove": r.literal(),
"field-to-update": "new-value"
})
You don't need to use replace here since you don't care about explicitly setting the other fields.
You can use replace with without and merge inside of your replace function:
r.table('30514947').get("492a41d2-d7dc-4440-8394-3633ae8ac337")
.replace(function (row) {
return row
.without("remove_field")
.merge({
"field-to-update": "hello"
})
})

How do I get unique field values using rethinkdb javascript?

I have a field which has similar values. For eg {country : 'US'} occurs multiple times in the table. Similar for other countries too. I want to return an array which contains non-redundant values of 'country' field. I am new to creating Databases so likely this is a trivial question but I couldn't find anything useful in rethinkdb api.[SOLVED]
Thanks
You can use distinct, but the distinct command was created for short sequences only.
If you have a lot of data, you can use map/reduce
r.table("data").map(function(doc) {
return r.object(doc("country"), true) // return { <country>: true}
}).reduce(function(left, right) {
return left.merge(right)
}).keys() // return all the keys of the final document

Resources