Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

RavenDB 4.1 Features: JavaScript Indexes

DZone's Guide to

RavenDB 4.1 Features: JavaScript Indexes

Read on to learn more about an experimental feature, RavenDB 4.1. This article shows a JavaScript index in RavenDB, and explains some of the performance implications.

· Database Zone ·
Free Resource

RavenDB vs MongoDB: Which is Better? This White Paper compares the two leading NoSQL Document Databases on 9 features to find out which is the best solution for your next project.  

Note: This feature is an experimental one. It will be included in 4.1, but it will be behind an experimental feature flag. It is possible that this will change before full inclusion in the product.

RavenDB now supports multiple operating systems, and we spend a lot of effort to bring RavenDB client APIs to more platforms. C#, JVM, and Python are already done and Go, Node.JS, and Ruby are in various beta stages. One of the things that this brought up was our indexing structure. Right now, if you want to define a custom index in RavenDB, you use C# Linq syntax to do so. When RavenDB was primarily focused on .NET, that was a perfectly fine decision. However, as we are pushing for more platforms, we wanted to avoid forcing users to learn the C# syntax when they create indexes.

Without further ado, here is a JavaScript index in RavenDB 4.1:

map('Users',  user => user.Name)

/*
Linq syntax:
  from user in docs.Users
  select new { user.Name }
*/

As you can see, this is a pretty simple translation between the two. It does make certain sets of operations easier since the JavaScript option is a lot more imperative. Consider the case of this more complex index:

map('Orders', order => {
  var emp = load(order.Employee, 'Employees');

  var total = order.Lines.reduce( 
    (sum, l) => sum + (l.Quantity * l.PricePerUnit),
    0);

  return {
    Employee: emp.FirstName + " " + emp.LastName,
    Company: order.Company,
    Total: total
  }

});

/* 
Linq syntax:
  from order in docs.Orders
  let emp = LoadDocument(order.Employee, "Employees")
  select new
  {
      Employee = emp.FirstName + " " + emp.LastName,
      order.Company,
      Total = order.Lines.Sum(l => (l.Quantity * l.PricePerUnit) ))
}
*/

You can see here the interplay of a few features. First, instead of just selecting a value to index, we can use a full-fledged function. That means that you can run your complex computation during index more easily. Features such as loading related documents are there, and you can see how we use reduce to aggregate information as part of the indexing function.

JavaScript’s dynamic nature gives us a lot of flexibility. If you want to index fields dynamically, just do so, as you can see here:

map('Employees', e => {
  var result = {};
  for (var key in e.Address) {
    result[key] = e.Address[key];
  }
  return result;
});

/*
Linq syntax:
  from e in docs.Employees
  select new
  {
    _ = e.Address.Select(field =>
      CreateField(field.Key, field.Value)
    )
  }
*/

MapReduce indexes work along the same concept. Here is a good example:

map('Orders', order => ({
    Company: order.Company,
    Count: 1,
    Total: order.Lines.reduce((sum, l) => sum + (l.Quantity * l.PricePerUnit), 0)
}));

groupBy(result => result.Company)
    .aggregate(g => ({
        Company: g.key,
        Count: g.values.reduce((acc, c) => acc + c.Count, 0),
        Total: g.values.reduce((acc, c) => acc + c.Total, 0)
    }));
/*
Linq syntax:
//map
  from order in docs.Orders
  select new
  {
      order.Company,
      Count = 1,
      Total = order.Lines.Sum(l => (l.Quantity * l.PricePerUnit))
  }

//reduce
  from result in results
  group result by result.Company 
  into g
  select new
  {
    Company = g.Key,
    Count = g.Sum(x => x.Count),
    Total = g.Sum(x => x.Total)
  }
*/

The indexing syntax is the only thing that changed. The rest is all the same. All the capabilities and features that you are used to are still there.

JavaScript is used extensively in RavenDB, not surprisingly. That is how you patch documents, do projections, and manage subscription. It is also a very natural language to handle JSON documents. I think that it is pretty fair to assume that anyone who uses RavenDB will have at least a passing familiarity with JavaScript, so that makes it easier to understand how indexing works.

There is also the security aspect. JavaScript is much easier to control and handle in an embedded fashion. The C# indexes allow users to write their own code that RavenDB will run. That code can, in theory, do anything. This is why index creation is an admin. level operation. With JavaScript indexes, we can allow users to run their computation without worrying that they will do something that they shouldn’t. Hence, the access level required for creating JavaScript indexes is much lower.

Using JavaScript for indexing does have some performance implications. The C# code is generally faster, but not much faster. The indexing function isn’t where we usually spend a lot of time when indexing, so adding a bit of additional work there (interpreting JavaScript) doesn’t hurt us too badly. We are able to get up to speeds of over 80,000 documents/second using JavaScript indexes, which should be sufficient. The C# indexes aren’t going anywhere, of course. They are still there and can provide additional flexibility/power as needed.

Another feature that might be very useful is the ability to attach additional sources to an index. For example, you may really like a sum using lodash. You can add the lodash.js file as an additional file to an index, and that would expose the library to the indexing functions.

Do you pay to use your database? What if your database paid you? Learn more with RavenDB.

Topics:
database ,ravendb

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}