Isar Plus
Recipes

String IDs

Learn how to use String UUIDs and maintain efficient integer IDs

String IDs

This is one of the most frequent requests I get, so here is a tutorial on using String ids.

Isar does not natively support String ids, and there is a good reason for it: integer ids are much more efficient and faster. Especially for links, the overhead of a String id is too significant.

Best of Both Worlds

I understand that sometimes you have to store external data that uses UUIDs or other non-integer ids. I recommend storing the String id as a property in your object and using a fast hash implementation to generate a 64-bit int that can be used as Id.

Implementation

Add String ID Field

Store your UUID or external ID as a regular string property.

Generate Integer ID

Use a fast hash function to convert the string to a 64-bit integer.

The integer ID will be used for efficient links and indexing.

user.dart
@collection
class User {
  String? id;

  Id get isarId => fastHash(id!);

  String? name;

  int? age;
}

With this approach, you get the best of both worlds: Efficient integer ids for links and the ability to use String ids.

Fast Hash Function

Ideally, your hash function should have high quality (you don't want collisions) and be fast. I recommend using the following implementation:

fast_hash.dart
/// FNV-1a 64bit hash algorithm optimized for Dart Strings
int fastHash(String string) {
  var hash = 0xcbf29ce484222325;

  var i = 0;
  while (i < string.length) {
    final codeUnit = string.codeUnitAt(i++);
    hash ^= codeUnit >> 8;
    hash *= 0x100000001b3;
    hash ^= codeUnit & 0xFF;
    hash *= 0x100000001b3;
  }

  return hash;
}

Hash Function Requirements

If you choose a different hash function, ensure it:

  • Returns a 64-bit integer
  • Has low collision rates
  • Is fast (avoid cryptographic hashes)
  • Is consistent across platforms

Platform Stability

Avoid using string.hashCode because it is not guaranteed to be stable across different platforms and versions of Dart.

Usage Example

example.dart
void main() async {
  final isar = Isar.open(schemas: [UserSchema]);

  // Create user with UUID
  final user = User()
    ..id = 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
    ..name = 'John Doe'
    ..age = 30;

  // The isarId getter automatically generates the integer ID
  await isar.writeAsync((isar) async {
    await isar.users.put(user);
  });

  // Query by string ID
  final foundUser = await isar.users
    .where()
    .idEqualTo('f47ac10b-58cc-4372-a567-0e02b2c3d479')
    .findFirst();

  // Or by integer ID for faster lookups
  final byIntId = await isar.users.get(fastHash(user.id!));
}

Alternative Hash Functions

MurmurHash3

int murmurHash3(String string) {
  var hash = 0x811c9dc5;
  for (var i = 0; i < string.length; i++) {
    hash ^= string.codeUnitAt(i);
    hash *= 0x01000193;
  }
  return hash;
}

CityHash (for longer strings)

For longer UUIDs or strings, consider using CityHash which has better collision resistance for longer inputs.

Performance Comparison

Hash FunctionSpeedCollision RateBest For
FNV-1a⚡⚡⚡LowGeneral use
MurmurHash3⚡⚡Very LowUUID/GUID
CityHashVery LowLong strings
String.hashCode⚡⚡⚡Platform dependent❌ Avoid

Recommendation

For most UUID/GUID scenarios, the FNV-1a implementation provided above offers the best balance of speed and collision resistance.

Last Update