ebbishop
ebbishop

Reputation: 1983

RethinkDB - Reduce results of list field

I have a dataset with a field that contains a list of strings for each document. I'm looking for a query that will give me the list of unique strings in that field

I thought reduce might be what I'm looking for:

r.db('test').table('entity')('listField').reduce(function (left, right) {
  var i;
  var l;
  for (i = 0, l = right.length; i < l; i ++) {
    if (left.not().contains(right[i])) {
      left.append(right[i]);
    }
  }
  return left;
}).default([])

But this only returns the listField of the first item in the list: ["C", "F", "G"]. Any document in the table can have any number of an unknown list of values in listField. How do I get the list of all available values?

Upvotes: 0

Views: 149

Answers (1)

Lyubomyr Shaydariv
Lyubomyr Shaydariv

Reputation: 21115

You have to remember that client side scripts cannot work in RethinkDB: these are just two very different scopes. RethinkDB only requires you to supply a ReQL expression that can be evaluated on the server side. By a ReQL expression I mean the stuff that can be found in the JavaScript ReQL command reference, and not client-side statements of any kind (statements, assignments, loops, branching, etc). reduce, similarly to all the rest of the terms, can accept a function that can build a new ReQL expression, and not meant to evaluate anything related to the query. In your case, the reduce function returns the left expression term (regardless your operations over the term -- they all create a new expression), and that's why you're getting your document listField value. As a result, expression builder functions are invoked before the query is actually executed.

Since you just want to find a unique value, you can rewrite the query and easily use RethinkDB built-ins like concatMap and distinct (these operations are very basic not just in RethinkDB):

//r.db('test').table('entity').delete();
//r.db('test').table('entity').insert([{
//  listField: ['A', 'B', 'B', 'C', 'C', 'C', 'C']
//}, {
//  listField: ['B', 'C', 'A', 'Z', 'Z', 'Z']
//}]);

r.db('test')
  .table('entity')
  .concatMap(r.row('listField'))
  .distinct();

And you'll obtain the result:

[
  "A",
  "B",
  "C",
  "Z"
]

The query above can be rewritten with an expression build function as well:

r.db('test')
  .table('entity')
  .concatMap(row => {
    // your local stuff here that is executed before the query is sent to the server and executed
    // note that this function must return a ReQL expression
    return row.getField('listField');
  })
  .distinct();

Upvotes: 1

Related Questions