Categories
JavaScript APIs

IndexedDB Manipulation with Dexie — Complex Queries

IndexedDB is a way to store data in the browser.

It lets us store larger amounts of data than local storage in an asynchronous way.

Dexie makes working with IndexedDB easier.

In this article, we’ll take a look at how to start working with IndexedDB with Dexie.

Query and Modify Data

We can query for and modify data at the same time.

For example, we can write:

(async () => {
  const db = new Dexie("friend_database");
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 68
    })
    await db.friends.put({
      name: "jane",
      age: 28
    })
    await db.friends
      .where('age')
      .inAnyRange([
        [0, 18],
        [65, Infinity]
      ])
      .modify({
        discount: 0.5
      });
  } catch (error) {
    console.log(error);
  }
})()

We get all the entries of the friends table with age between 0 to 18 or 65 and up.

Then we call modify to change the discount field to 0.5.

Search for Entries with Any Condition

We can search for entries with our own condition with the filter method.

For example, we can write:

(async () => {
  const db = new Dexie("friend_database");
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 68
    })
    await db.friends.put({
      name: "jane",
      age: 28
    })
    const someFriends = await db.friends
      .filter(friend => /j/i.test(friend.name))
      .toArray();
    console.log(someFriends)
  } catch (error) {
    console.log(error);
  }
})()

We call filter with a callback that checks the friend.name value to see if it has the letter j in it.

Also, we can search for data with compound indexes.

For instance, we can write:

(async () => {
  const db = new Dexie("friend_database");
  try {
    await db.version(1).stores({
      friends: '++id,[firstName+lastName],age'
    });
    await db.friends.put({
      firstName: "mary",
      lastName: 'smith',
      age: 39
    })
    await db.friends.put({
      firstName: "jane",
      lastName: 'wong',
      age: 28
    })
    const someFriends = await db.friends
      .where('[firstName+lastName]')
      .equals(["jane", "wong"])
      .first();
    console.log(someFriends)
  } catch (error) {
    console.log(error);
  }
})()

We have the compound index [firstName+lastName] .

Then we can search for both fields together with the where method with the compound fields.

And equals with the value of each.

first returns the first result.

We search for the value of firstName and lastName together with equals .

We can make this query simpler with the where method:

(async () => {
  const db = new Dexie("friend_database");
  try {
    await db.version(1).stores({
      friends: '++id,[firstName+lastName],age'
    });
    await db.friends.put({
      firstName: "mary",
      lastName: 'smith',
      age: 39
    })
    await db.friends.put({
      firstName: "jane",
      lastName: 'wong',
      age: 28
    })
    const someFriends = await db.friends
      .where({
        firstName: "jane",
        lastName: "wong"
      })
      .first();
    console.log(someFriends)
  } catch (error) {
    console.log(error);
  }
})()

We can also use the get method to call where and first together:

(async () => {
  const db = new Dexie("friend_database");
  try {
    await db.version(1).stores({
      friends: '++id,[firstName+lastName],age'
    });
    await db.friends.put({
      firstName: "mary",
      lastName: 'smith',
      age: 39
    })
    await db.friends.put({
      firstName: "jane",
      lastName: 'wong',
      age: 28
    })
    const someFriends = await db.friends
      .get({
        firstName: "jane",
        lastName: "wong"
      })
    console.log(someFriends)
  } catch (error) {
    console.log(error);
  }
})()

Conclusion

We can make queries that modify data, queries that have more complex conditions, and queries with compound indexes with Dexie.

Categories
JavaScript APIs

IndexedDB Manipulation with Dexie — Delete Items and Queries

IndexedDB is a way to store data in the browser.

It lets us store larger amounts of data than local storage in an asynchronous way.

Dexie makes working with IndexedDB easier.

In this article, we’ll take a look at how to start working with IndexedDB with Dexie.

Delete Items

We can an item by its primary key with the delete method.

For example, we can write:

(async () => {
  const db = new Dexie("friend_database");
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    const friend = await db.friends.get('mary');
    await db.friends.delete('mary');
  } catch (error) {
    console.log(error);
  }
})()

We create an object with primary key 'mary' , which is the value of the name column.

Then we call delete to delete the item by its primary key.

We can call bulkDelete to delete multiple items with their primary keys.

For example, we can write:

(async () => {
  const db = new Dexie("friend_database");
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    await db.friends.put({
      name: "jane",
      age: 28
    })
    const friend = await db.friends.get('mary');
    await db.friends.bulkDelete(['mary', 'jane']);
  } catch (error) {
    console.log(error);
  }
})()

We create friends entries with primary key 'mary' and 'jane' .

Then we can delete them both with the bulkDelete method.

Query Items

We can query items with various methods.

For example, we can write:

(async () => {
  const db = new Dexie("friend_database");
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    await db.friends.put({
      name: "jane",
      age: 28
    })
    const someFriends = await db.friends
      .where("age")
      .between(20, 30)
      .offset(0)
      .limit(25)
      .toArray();
    console.log(someFriends)
  } catch (error) {
    console.log(error);
  }
})()

The where method takes the field we’re searching in.

between takes the arguments we want to search between.

offset lets us skip the first number of items given the arguments.

limit limits the number of results returned.

toArray converts the results to an array.

We can also loop through the results with the each method.

For instance, we can write:

(async () => {
  const db = new Dexie("friend_database");
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    await db.friends.put({
      name: "jane",
      age: 28
    })
    const someFriends = await db.friends
      .where("name")
      .equalsIgnoreCase("mary")
      .each(friend => {
        console.log(friend);
      });
  } catch (error) {
    console.log(error);
  }
})()

We call equalsIgnoreCase to find the name with the given value ignoring the case.

Then we use each to loop through each result.

We can do a case-insensitive search for values of a field that starts with any one of the given substrings with the startsWithAnyOfIgnoreCase method.

For example, we can write:

(async () => {
  const db = new Dexie("friend_database");
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    await db.friends.put({
      name: "jane",
      age: 28
    })
    const someFriends = await db.friends
      .where("name")
      .startsWithAnyOfIgnoreCase(['m', 'j'])
      .toArray();
    console.log(someFriends)
  } catch (error) {
    console.log(error);
  }
})()

to search for values of name that starts with 'm' or 'j' .

Conclusion

We can delete items from our tables and query for data in our IndexedDB database with Dexie.

Categories
JavaScript APIs

IndexedDB Manipulation with Dexie — CRUD Operations

%2F%2Funsplash.com%3Futm_source%3Dmedium%26utm_medium%3Dreferral)

IndexedDB is a way to store data in the browser.

It lets us store larger amounts of data than local storage in an asynchronous way.

Dexie makes working with IndexedDB easier.

In this article, we’ll take a look at how to start working with IndexedDB with Dexie.

Declare Database

We declare our IndexedDB database with the Dexie class.

For example, we write:

(async () => {
  var db = new Dexie("MyDatabase");
  db.version(1).stores({
    friends: "++id, name, age, *tags",
    records: "id, score"
  });
})()

friends and records are the tables.

The property strings are the columns that we want to index.

We shouldn’t declare all columns like in SQL.

The string has some special syntax. They include:

  • + — Auto-incremented primary key
  • & — Unique
  • * — Multi-entry index
  • [A+B] — Compound index

Class Binding

We can map our entries to an instance of a class by writing:

class Friend {
  save() {
    return db.friends.put(this);
  }

get age() {
    return moment(Date.now()).diff(this.birthDate, 'years');
  }
}

(async () => {
  const db = new Dexie("MyDatabase");
  await db.version(1).stores({
    friends: "++id, name, age, *tags",
  });
  db.friends.mapToClass(Friend);
})()

We call mapToClass to map our friends entries to Friend class instances.

Add Items

To add items into our database, we call the add method.

For example, we can write:

(async () => {
  const db = new Dexie("MyDatabase");
  await db.version(1).stores({
    friends: "++id, name, age, *tags",
  });
  await db.friends.add({
    name: "joe",
    age: 21
  });
})()

to add an entry to the friends table.

We can add multiple entries at once with the bulkAdd method:

(async () => {
  const db = new Dexie("MyDatabase");
  await db.version(1).stores({
    friends: "++id, name, age, *tags",
  });
  await db.friends.bulkAdd([{
      name: "alex",
      age: 31
    },
    {
      name: "mary",
      age: 32
    }
  ]);
})()

Update Items

We update items in our database with the put method.

If it doesn’t exist, put will also add new objects to the store.

For example, we can write:

(async () => {
  const db = new Dexie("MyDatabase");
  await db.version(1).stores({
    friends: "++id, name, age, *tags",
  });
  await db.friends.put({
    id: 4,
    name: "jane",
    age: 33
  });

})()

We can use bulkPut to call put with multiple objects.

For example, we can write:

(async () => {
  const db = new Dexie("MyDatabase");
  await db.version(1).stores({
    friends: "++id, name, age, *tags",
  });
  await db.friends.bulkPut([{
      id: 4,
      name: "bob",
      age: 34
    },
    {
      id: 5,
      name: "may",
      age: 44
    }
  ]);

})()

to add or update both entries.

If we just want to update an entry, we can use the update method.

For example, we can write:

(async () => {
  const db = new Dexie("MyDatabase");
  await db.version(1).stores({
    friends: "++id, name, age, *tags",
  });
  await db.friends.update(4, {
    name: "peter"
  });
})()

to update entries with id 4 to with the new field values.

We can also call modify to modify any entry that is returned from queries:

(async () => {
  const db = new Dexie("MyDatabase");
  await db.version(1).stores({
    friends: "++id, name, age, discount",
  });
  await db.friends.bulkAdd([{
      name: "alex",
      age: 17
    },
    {
      name: "mary",
      age: 32
    },
    {
      name: "don",
      age: 70
    }
  ]);
  const results = await db.friends
    .where("age")
    .inAnyRange([
      [0, 18],
      [65, Infinity]
    ])
  await results.modify(friend => {
    friend.discount = 0.3;
  });
})()

We get the results from the query. where takes a field name.

inAnyRange lets us search for values in the given ranges for the given field name.

The modify modifies the returned results.

Conclusion

Categories
JavaScript APIs

IndexedDB Manipulation with Dexie — Queries and Indexes

IndexedDB is a way to store data in the browser.

It lets us store larger amounts of data than local storage in an asynchronous way.

Dexie makes working with IndexedDB easier.

In this article, we’ll take a look at how to start working with IndexedDB with Dexie.

Queries

We can retrieve objects from our table with the get and where methods.

get retrieves an object by its primary key.

where does an advanced query.

For example, we can use get by writing:

const db = new Dexie("friend_database");
(async () => {
  try {
    await db.version(1).stores({
      friends: '++id,name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    await db.friends.put({
      name: "james",
      age: 22
    })
    const friend = await db.friends.get(1)
    console.log(friend)
  } catch (error) {
    console.log(error);
  }
})()

We call get with the id value of the entry to get.

Then that returns a promise with the result we want to get.

To make more complex queries, we can use the where query.

For instance, we can write:

const db = new Dexie("friend_database");
(async () => {
  try {
    await db.version(1).stores({
      friends: '++id,name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    await db.friends.put({
      name: "james",
      age: 22
    })
    const friendCount = await db.friends.where('age').above(25).count()
    console.log(friendCount)
  } catch (error) {
    console.log(error);
  }
})()

We add 2 entries to the friends table.

Then we call where with the column to search for.

above searches for anything with the value above a given value.

count returns the count of the results.

We can call more methods to make more advanced queries:

const db = new Dexie("friend_database");
(async () => {
  try {
    await db.version(1).stores({
      friends: '++id,name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28,
      isCloseFriend: true
    })
    await db.friends.put({
      name: "james",
      age: 22
    })
    const friendCount = await db.friends
      .where('age')
      .between(37, 40)
      .or('name')
      .anyOf(['mary', 'james'])
      .and((friend) => {
        return friend.isCloseFriend;
      })
      .limit(10)
      .each((friend) => {
        console.log(friend);
      });
  } catch (error) {
    console.log(error);
  }
})()

or lets us combine one or more conditions with an OR operator.

anyOf searches for any of the values in the array.

and combines one or more conditions with an AND operator.

limit limits the number of items returned.

each lets us iterate through each result.

Detailed Schema Syntax

We can define a schema with Dexie’s schema syntax.

Parts of the syntax include:

  • ++keyPath — autoincrement primary key.
  • ++ — hidden autoincrement primary key.
  • keyPath — non-autoincrement primary key.
  • (blank) — hidden primary key.
  • keyPath — the keyPath is indexed
  • &keyPath — keyPath is indexed and the keys must be unique.
  • *keyPath — the key is an array and eah array value is regarded as a key to the object
  • [keyPath1+keyPath2] — compound index for keyPath1 and keyPath2 .

We can use them to add indexes by writing:

(async () => {
  const db = new Dexie('MyDatabase');
  db.version(1).stores({
    friends: '++id,name,age',
    pets: 'id, name, kind',
    cars: '++, name',
    enemies: ',name,*weaknesses',
    users: 'meta.ssn, addr.city',
    people: '[name+id], &id'
  });
})()

Conclusion

We can make queries and create indexes easily in our IndexedDB database with Dexie.

Categories
JavaScript APIs

IndexedDB Manipulation with Dexie — Indexes, Seed Data, and Promises

IndexedDB is a way to store data in the browser.

It lets us store larger amounts of data than local storage in an asynchronous way.

Dexie makes working with IndexedDB easier.

In this article, we’ll take a look at how to start working with IndexedDB with Dexie.

Indexes

We can add and drop indexes as we wish.

For example, we write:

const db = new Dexie("dbs");
db.version(1).stores({
  foo1: 'id,x,y,z',
  foo2: 'id,x,y,z',
  foo3: 'id,x,y,z'
});
db.version(2).stores({
  foo1: 'id,x,z'
});
db.version(3).stores({
  foo2: 'id, x, x2, y, z'
});
db.version(4).stores({
  foo3: null
});

We created the foo1 , foo2 , and foo3 stores with indexes x , y and z .

Then to delete index y from foo1 , we write:

db.version(2).stores({
  foo1: 'id,x,z'
});

To add index x2 to foo2 , we write:

db.version(3).stores({
  foo2: 'id, x, x2, y, z'
});

And to drop table foo3 , we write:

db.version(4).stores({
  foo3: null
});

The populate Event

If we need initial data to be added to our database, we can watch for the populate event and add data initial data in the callback for the event.

For example, we write:

(async () => {
  const db = new Dexie("orders_database");
  await db.version(1).stores({
    orders: "++id,headline,description,statusId",
    statuses: "++id,name,openess"
  });

  db.on("populate", async () => {
    await db.statuses.add({
      id: 1,
      name: "opened",
      openess: true
    });
    await db.statuses.add({
      id: 2,
      name: "cancelled",
      openess: false
    });
    await db.statuses.add({
      id: 3,
      name: "shipped",
      openess: false
    });
    await db.statuses.add({
      id: 4,
      name: "delivered",
      openess: false
    });
  });
})()

to listen to the populate event and add some data when it’s emitted.

Promises

Dexie comes with a promise-based API.

For example, we can write:

const db = new Dexie("friend_database");
(async () => {
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    const friend = await db.friends.get('mary');
    const arr = await db.friends.where('name').startsWithIgnoreCase('mary').toArray();
    console.log(arr)
  } catch (error) {
    console.log(error);
  }
})()

We create the friends_database database with the friends table.

Then we add some data to the friends store with the db.friends.put method.

Then we get the data with the db.friends.where method with the column name we’re searching.

startsIgnoreCase lets us search by column by the text it starts with.

toArray returns a promise that resolves to an array of results.

Then we log the data with console.log .

All Dexie async methods return a promise.

For example, if we have:

const db = new Dexie("friend_database");
(async () => {
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    const friend = await db.friends.get('mary');
    const arr = await db.friends.toArray();
    console.log(arr)
  } catch (error) {
    console.log(error);
  }
})()

That’s the same as:

const db = new Dexie("friend_database");
(async () => {
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    const friend = await db.friends.get('mary');
    db.friends.toArray(result => console.log(result));
  } catch (error) {
    console.log(error);
  }
})()

But the promise is more convenient.

Conclusion

We can add index, add initial data, and use the promise API provided by Dexie.