Working with Indexes

Learn how to use different indexes efficiently by going through the ArangoDB Performance Course.

Index Identifiers and Handles

An index handle uniquely identifies an index in the database. It is a string and consists of the collection name and an index identifier separated by a /. The index identifier part is a numeric value that is auto-generated by ArangoDB.

A specific index of a collection can be accessed using its index handle or index identifier as follows:

  1. db.collection.index("<index-handle>");
  2. db.collection.index("<index-identifier>");
  3. db._index("<index-handle>");

For example: Assume that the index handle, which is stored in the _id attribute of the index, is demo/362549736 and the index was created in a collection named demo. Then this index can be accessed as:

  1. db.demo.index("demo/362549736");

Because the index handle is unique within the database, you can leave out the collection and use the shortcut:

  1. db._index("demo/362549736");

An index may also be looked up by its name. Since names are only unique within a collection, rather than within the database, the lookup must also include the collection name.

  1. db._index("demo/primary")
  2. db.demo.index("primary")

Collection Methods

Listing all indexes of a collection

returns information about the indexes getIndexes()

Returns an array of all indexes defined for the collection. Since ArangoDB 3.4, indexes() is an alias for getIndexes().

Note that _key implicitly has an index assigned to it.

  1. arangosh> db.test.ensureIndex({ type: "persistent", fields: [
  2. ........> "attribute", "secondAttribute.subAttribute"] });
  3. arangosh> db.test.getIndexes();

Show execution results

Hide execution results

  1. {
  2. "deduplicate" : true,
  3. "estimates" : true,
  4. "fields" : [
  5. "attribute",
  6. "secondAttribute.subAttribute"
  7. ],
  8. "id" : "test/70648",
  9. "isNewlyCreated" : true,
  10. "name" : "idx_1707084123965751296",
  11. "selectivityEstimate" : 1,
  12. "sparse" : false,
  13. "type" : "persistent",
  14. "unique" : false,
  15. "code" : 201
  16. }
  17. [
  18. {
  19. "fields" : [
  20. "_key"
  21. ],
  22. "id" : "test/0",
  23. "name" : "primary",
  24. "selectivityEstimate" : 1,
  25. "sparse" : false,
  26. "type" : "primary",
  27. "unique" : true
  28. },
  29. {
  30. "deduplicate" : true,
  31. "estimates" : true,
  32. "fields" : [
  33. "attribute"
  34. ],
  35. "id" : "test/70640",
  36. "name" : "idx_1707084123963654144",
  37. "selectivityEstimate" : 1,
  38. "sparse" : false,
  39. "type" : "persistent",
  40. "unique" : true
  41. },
  42. {
  43. "deduplicate" : true,
  44. "estimates" : true,
  45. "fields" : [
  46. "uniqueAttribute"
  47. ],
  48. "id" : "test/70644",
  49. "name" : "idx_1707084123964702720",
  50. "selectivityEstimate" : 1,
  51. "sparse" : false,
  52. "type" : "persistent",
  53. "unique" : true
  54. },
  55. {
  56. "deduplicate" : true,
  57. "estimates" : true,
  58. "fields" : [
  59. "attribute",
  60. "secondAttribute.subAttribute"
  61. ],
  62. "id" : "test/70648",
  63. "name" : "idx_1707084123965751296",
  64. "selectivityEstimate" : 1,
  65. "sparse" : false,
  66. "type" : "persistent",
  67. "unique" : false
  68. }
  69. ]

Creating an index

ensures that an index exists collection.ensureIndex(index-description)

Ensures that an index according to the index-description exists. A new index will be created if none exists with the given description.

The index-description must contain at least a type attribute. Other attributes may be necessary, depending on the index type.

type can be one of the following values:

  • persistent: persistent index
  • fulltext: fulltext index
  • geo: geo index, with one or two attributes

name can be a string. Index names are subject to the same character restrictions as collection names. If omitted, a name will be auto-generated so that it is unique with respect to the collection, e.g. idx_832910498.

sparse can be true or false.

For persistent the sparsity can be controlled, fulltext and geo are sparse by definition.

unique can be true or false and is supported by persistent

Calling this method returns an index object. Whether or not the index object existed before the call is indicated in the return attribute isNewlyCreated.

deduplicate can be true or false and is supported by array indexes of type persistent. It controls whether inserting duplicate index values from the same document into a unique array index will lead to a unique constraint error or not. The default value is true, so only a single instance of each non-unique index value will be inserted into the index per document. Trying to insert a value into the index that already exists in the index will always fail, regardless of the value of this attribute.

estimates can be true or false and is supported by indexes of type persistent. This attribute controls whether index selectivity estimates are maintained for the index. Not maintaining index selectivity estimates can have a slightly positive impact on write performance. The downside of turning off index selectivity estimates will be that the query optimizer will not be able to determine the usefulness of different competing indexes in AQL queries when there are multiple candidate indexes to choose from. The estimates attribute is optional and defaults to true if not set. It will have no effect on indexes other than persistent (with hash and skiplist being mere aliases for persistent nowadays).

Examples

  1. arangosh> db.test.ensureIndex({ type: "persistent", fields: [ "a" ], sparse: true });
  2. arangosh> db.test.ensureIndex({ type: "persistent", fields: [ "a", "b" ], unique: true });

Show execution results

Hide execution results

  1. {
  2. "deduplicate" : true,
  3. "estimates" : true,
  4. "fields" : [
  5. "a"
  6. ],
  7. "id" : "test/70606",
  8. "isNewlyCreated" : true,
  9. "name" : "idx_1707084123944779777",
  10. "selectivityEstimate" : 1,
  11. "sparse" : true,
  12. "type" : "persistent",
  13. "unique" : false,
  14. "code" : 201
  15. }
  16. {
  17. "deduplicate" : true,
  18. "estimates" : true,
  19. "fields" : [
  20. "a",
  21. "b"
  22. ],
  23. "id" : "test/70610",
  24. "isNewlyCreated" : true,
  25. "name" : "idx_1707084123945828352",
  26. "selectivityEstimate" : 1,
  27. "sparse" : false,
  28. "type" : "persistent",
  29. "unique" : true,
  30. "code" : 201
  31. }

Dropping an index via a collection handle

drops an index collection.dropIndex(index)

Drops the index. If the index does not exist, then false is returned. If the index existed and was dropped, then true is returned. Note that you cannot drop some special indexes (e.g. the primary index of a collection or the edge index of an edge collection).

collection.dropIndex(index-handle)

Same as above. Instead of an index an index handle can be given.

  1. arangosh> db.example.ensureIndex({ type: "persistent", fields: ["a", "b"] });
  2. arangosh> var indexInfo = db.example.getIndexes();
  3. arangosh> indexInfo;
  4. arangosh> db.example.dropIndex(indexInfo[0])
  5. arangosh> db.example.dropIndex(indexInfo[1].id)
  6. arangosh> indexInfo = db.example.getIndexes();

Show execution results

Hide execution results

  1. {
  2. "deduplicate" : true,
  3. "estimates" : true,
  4. "fields" : [
  5. "a",
  6. "b"
  7. ],
  8. "id" : "example/70479",
  9. "isNewlyCreated" : true,
  10. "name" : "idx_1707084123865088001",
  11. "selectivityEstimate" : 1,
  12. "sparse" : false,
  13. "type" : "persistent",
  14. "unique" : false,
  15. "code" : 201
  16. }
  17. [
  18. {
  19. "fields" : [
  20. "_key"
  21. ],
  22. "id" : "example/0",
  23. "name" : "primary",
  24. "selectivityEstimate" : 1,
  25. "sparse" : false,
  26. "type" : "primary",
  27. "unique" : true
  28. },
  29. {
  30. "deduplicate" : true,
  31. "estimates" : true,
  32. "fields" : [
  33. "a",
  34. "b"
  35. ],
  36. "id" : "example/70479",
  37. "name" : "idx_1707084123865088001",
  38. "selectivityEstimate" : 1,
  39. "sparse" : false,
  40. "type" : "persistent",
  41. "unique" : false
  42. }
  43. ]
  44. false
  45. true
  46. [
  47. {
  48. "fields" : [
  49. "_key"
  50. ],
  51. "id" : "example/0",
  52. "name" : "primary",
  53. "selectivityEstimate" : 1,
  54. "sparse" : false,
  55. "type" : "primary",
  56. "unique" : true
  57. }
  58. ]

Load Indexes into Memory

Loads all indexes of this collection into Memory. collection.loadIndexesIntoMemory()

This function tries to cache all index entries of this collection into the main memory. Therefore it iterates over all indexes of the collection and stores the indexed values, not the entire document data, in memory. All lookups that could be found in the cache are much faster than lookups not stored in the cache so you get a nice performance boost. It is also guaranteed that the cache is consistent with the stored data.

This function honors memory limits. If the indexes you want to load are smaller than your memory limit this function guarantees that most index values are cached. If the index is larger than your memory limit this function will fill up values up to this limit and for the time being there is no way to control which indexes of the collection should have priority over others.

  1. arangosh> db.example.loadIndexesIntoMemory();

Show execution results

Hide execution results

  1. {
  2. "result" : true
  3. }

Database Methods

Fetching an index by handle

finds an index db._index(index-handle)

Returns the index with index-handle or null if no such index exists.

  1. arangosh> db.example.ensureIndex({ type: "persistent", fields: [ "a", "b" ] });
  2. arangosh> var indexInfo = db.example.getIndexes().map(function(x) { return x.id; });
  3. arangosh> indexInfo;
  4. arangosh> db._index(indexInfo[0])
  5. arangosh> db._index(indexInfo[1])

Show execution results

Hide execution results

  1. {
  2. "deduplicate" : true,
  3. "estimates" : true,
  4. "fields" : [
  5. "a",
  6. "b"
  7. ],
  8. "id" : "example/66088",
  9. "isNewlyCreated" : true,
  10. "name" : "idx_1707084068619812864",
  11. "selectivityEstimate" : 1,
  12. "sparse" : false,
  13. "type" : "persistent",
  14. "unique" : false,
  15. "code" : 201
  16. }
  17. [
  18. "example/0",
  19. "example/66088"
  20. ]
  21. {
  22. "fields" : [
  23. "_key"
  24. ],
  25. "id" : "example/0",
  26. "name" : "primary",
  27. "sparse" : false,
  28. "type" : "primary",
  29. "unique" : true,
  30. "code" : 200
  31. }
  32. {
  33. "deduplicate" : true,
  34. "estimates" : true,
  35. "fields" : [
  36. "a",
  37. "b"
  38. ],
  39. "id" : "example/66088",
  40. "name" : "idx_1707084068619812864",
  41. "sparse" : false,
  42. "type" : "persistent",
  43. "unique" : false,
  44. "code" : 200
  45. }

Dropping an index via a database handle

drops an index db._dropIndex(index)

Drops the index. If the index does not exist, then false is returned. If the index existed and was dropped, then true is returned.

db._dropIndex(index-handle)

Drops the index with index-handle.

  1. arangosh> db.example.ensureIndex({ type: "persistent", fields: [ "a", "b" ] });
  2. arangosh> var indexInfo = db.example.getIndexes();
  3. arangosh> indexInfo;
  4. arangosh> db._dropIndex(indexInfo[0])
  5. arangosh> db._dropIndex(indexInfo[1].id)
  6. arangosh> indexInfo = db.example.getIndexes();

Show execution results

Hide execution results

  1. {
  2. "deduplicate" : true,
  3. "estimates" : true,
  4. "fields" : [
  5. "a",
  6. "b"
  7. ],
  8. "id" : "example/71060",
  9. "isNewlyCreated" : true,
  10. "name" : "idx_1707084124174417920",
  11. "selectivityEstimate" : 1,
  12. "sparse" : false,
  13. "type" : "persistent",
  14. "unique" : false,
  15. "code" : 201
  16. }
  17. [
  18. {
  19. "fields" : [
  20. "_key"
  21. ],
  22. "id" : "example/0",
  23. "name" : "primary",
  24. "selectivityEstimate" : 1,
  25. "sparse" : false,
  26. "type" : "primary",
  27. "unique" : true
  28. },
  29. {
  30. "deduplicate" : true,
  31. "estimates" : true,
  32. "fields" : [
  33. "a",
  34. "b"
  35. ],
  36. "id" : "example/71060",
  37. "name" : "idx_1707084124174417920",
  38. "selectivityEstimate" : 1,
  39. "sparse" : false,
  40. "type" : "persistent",
  41. "unique" : false
  42. }
  43. ]
  44. false
  45. true
  46. [
  47. {
  48. "fields" : [
  49. "_key"
  50. ],
  51. "id" : "example/0",
  52. "name" : "primary",
  53. "selectivityEstimate" : 1,
  54. "sparse" : false,
  55. "type" : "primary",
  56. "unique" : true
  57. }
  58. ]

Revalidating whether an index is used

finds an index

So you’ve created an index, and since its maintenance isn’t for free, you definitely want to know whether your query can utilize it.

You can use explain to verify that a certain index is used:

  1. arangosh> var explain = require("@arangodb/aql/explainer").explain;
  2. arangosh> db.example.ensureIndex({ type: "skiplist", fields: [ "a", "b" ] });
  3. arangosh> explain("FOR doc IN example FILTER doc.a < 23 RETURN doc", {colors: false});

Show execution results

Hide execution results

  1. {
  2. "deduplicate" : true,
  3. "estimates" : true,
  4. "fields" : [
  5. "a",
  6. "b"
  7. ],
  8. "id" : "example/66102",
  9. "isNewlyCreated" : true,
  10. "name" : "idx_1707084068628201472",
  11. "selectivityEstimate" : 1,
  12. "sparse" : false,
  13. "type" : "skiplist",
  14. "unique" : false,
  15. "code" : 201
  16. }
  17. Query String (47 chars, cacheable: true):
  18. FOR doc IN example FILTER doc.a < 23 RETURN doc
  19. Execution plan:
  20. Id NodeType Est. Comment
  21. 1 SingletonNode 1 * ROOT
  22. 6 IndexNode 0 - FOR doc IN example /* skiplist index scan */
  23. 5 ReturnNode 0 - RETURN doc
  24. Indexes used:
  25. By Name Type Collection Unique Sparse Selectivity Fields Ranges
  26. 6 idx_1707084068628201472 skiplist example false false 100.00 % [ `a`, `b` ] (doc.`a` < 23)
  27. Optimization rules applied:
  28. Id RuleName
  29. 1 use-indexes
  30. 2 remove-filter-covered-by-index
  31. 3 remove-unnecessary-calculations-2
  32. Optimization rules with highest execution times:
  33. RuleName Duration [s]
  34. use-indexes 0.00003
  35. remove-filter-covered-by-index 0.00001
  36. reduce-extraction-to-projection 0.00000
  37. remove-unnecessary-calculations-2 0.00000
  38. optimize-subqueries 0.00000
  39. 41 rule(s) executed, 1 plan(s) created

(If you omit colors: false you will get nice colors in ArangoShell.)