database 0.3.1 database: ^0.3.1 copied to clipboard
A vendor-agnostic database access API. Supports many technologies/products such as browser APIs, SQLite, Google Cloud Firestore, PostgreSQL, ElasticSearch, and Algolia.
Introduction #
This is database.dart, a vendor-agnostic database access API for Flutter and other Dart projects.
This version is just an early preview. Major changes are possible during the early development. Anyone is welcome to contribute to the development of this package.
Licensed under the Apache License 2.0.
Why this package? #
- 👫 Document & SQL database support. The API has been designed to support both SQL databases and document databases. You - or your customers - can always choose the best database without rewriting any code.
- 🔭 Full-text search engine support. The API supports forwarding specific queries to search engines that can, for example, handle natural language queries better than transaction databases. There are already several search engines already supported (Algolia, ElasticSearch, and a simple search engine written in Dart).
Links #
Issues? #
- Report issues at the issue tracker.
- Contributing a fix? Fork the repository, do your changes, and just create a pull request in Github. Key contributors will be invited to become project administrators in Github.
Supported products and APIs #
Document databases #
- Azure Cosmos DB (website)
- Package (not ready for use): database_adapter_azure (Github
- Google Cloud Firestore (website)
- Browser package: database_adapter_firestore_browser (Github)
- Flutter (iOS /Android) package: database_adapter_firestore_flutter (Github)
- Have a package? Add it here!
SQL databases #
- PostgreSQL (website)
- Package: database_adapter_postgre (Github)
- SQLite (website)
- Package: database_adapter_sqlite (Github)
- Have a package? Add it here!
Search engines #
- Algolia (website)
- Package: database_adapter_algolia (Github)
- Azure Cognitive Search (search)
- Package (not ready for use): database_adapter_azure (Github
- Elasticsearch (website))
- Package: database_adapter_elasticsearch (Github)
- Have a package? Add it here!
Other #
- Web APIs
- BrowserDatabaseAdapter (Github) uses the best available web API.
- LocalStorageDatabaseAdapter (Github).
- Memory
- MemoryDatabaseAdapter (Github) keeps data in memory. Good for tests and caching.
- Have a package? Add it here!
Middleware #
- CachingDatabaseAdapter (Github)
- Caches data in another database (such as MemoryDatabaseAdapter).
- SchemaEnforcingDatabaseAdapter (Github)
- Enforces schemas on reads/writes.
- SearchEnginePromotingDatabaseAdapter (Github)
- Forwards cache-accepting search requests to a search engine.
- SearchingDatabaseAdapter in package search (Github) provides minimalistic search engine for small collections.
- Have a package? Add it here!
Getting started #
1.Add dependency #
In pubspec.yaml
, add:
dependencies:
database: any
2.Choose adapter #
Look at the earlier list of adapters.
For example:
import 'package:database/database.dart';
final Database database = MemoryDatabaseAdapter().database();
Document-style API #
Overview #
If you have used some other document-oriented API (such as Google Firestore), this API will feel familiar to you. A database is made of document collection. A document is an arbitrary tree of values that may contain references to other documents.
For example, this is how you would store a recipe:
var food = {
'name': 'Spaghetti Bolognese',
'rating': 4.5,
'ingredients': ['pasta', 'minced meat'],
'similar': [
database.collection('foods').document('spaghetti_carbonara'),
],
};
database.collection('foods').document('spaghetti_bolognese').upsert(food);
The following data types are currently supported by document database API:
null
bool
int
- Int64
double
- Date
DateTime
- Timestamp
- GeoPoint
String
Uint8List
List
Map<String,Object>
- Document (a reference to another document)
Writing documents #
// Allocate a document with a random 128-bit identifier
final document = database.collection('example').newDocument();
// Upsert, which means "inserting or updating".
await document.upsert({
'any property': 'any value',
});
// Delete
await document.delete();
If you want to write only if the document doesn't exist, use insert
:
// Insert
final product = database.collection('product').insert({
'name: 'Coffee mug',
'price': 8,
});
If you want to write only if the document already exists, use update
:
await product.update(
{
'name': 'Coffee mug',
'price': 12,
},
);
Reading documents #
get() #
// Read a snapshot from a regional master database.
// If it's acceptable to have a locally cached version, use Reach.local.
final snapshot = await document.get(reach: Reach.regional);
// Use 'exists' to check whether the document exists
if (snapshot.exists) {
final price = snapshot.data['price'];
print('price: $price');
}
watch() #
By using watch
function, you continue to receive updates to the document. Some databases support
this natively. In other databases, watching may be accomplished by polling.
final stream = await document.watch(
pollingInterval: Duration(seconds:2),
reach: Reach.server,
);
Searching #
Search products with descriptions containing 'milk' or 'vegetables':
final result = await database.collection('product').search(
query: Query.parse('description:(bread OR vegetables)'),
reach: Reach.server,
);
for (var snapshot in result.snapshots) {
// ...
}
Available filters #
The following logical operations are supported:
AndFilter([ValueFilter('f0'), ValueFilter('f1')])
OrFilter([ValueFilter('f0'), ValueFilter('f1')])
NotFilter(ValueFilter('example'))
The following primitives supported:
- List
ListFilter(items: ValueFilter('value'))
- Map
MapFilter({'key': ValueFilter('value')})
- Comparisons
ValueFilter(3.14)
RangeFilter(min:3, max:4)
RangeFilter(min:3, max:4, isExclusiveMin:true, isExclusiveMax:true)
RangeFilter(min:3, max:4, isExclusiveMin:true, isExclusiveMax:true)
- Geospatial
- [GeoPointFilter]
- Example:
GeoPointFilter(near:GeoPoint(1.23, 3.45), maxDistance:1000)
- Example:
- [GeoPointFilter]
The following special filter types are also supported:
- SQL query
- Example:
SqlFilter('SELECT * FROM hotels WHERE breakfast = ?, price < ?', [true, 100])
- Should be only in the root level of the query.
- Example:
- Natural language search query
- Examples:
KeywordFilter('example')
- Keyword queries (
KeyFilter
) do not usually work unless you have configured a search engine for your application.
- Examples:
Using SQL client #
By using SqlClient, you can interact with the database using SQL:
import 'package:database/sql.dart';
import 'package:database_adapter_postgre/database_adapter_postgre.dart';
Future main() async {
// In this example, we use PostgreSQL adapter
final database = Postgre(
host: 'localhost',
user: 'database user',
password: 'database password',
databaseName: 'example',
).database();
// Construct SQL client.
final sqlClient = database.sqlClient;
// Select all pizza products with price less than 10.
//
// This will return a value of type:
// Iterable<Map<String,Object>>
final pizzas = await sqlClient.query(
'SELECT * FROM product WHERE type = ?, price < ?',
['pizza', 10],
).toMaps();
for (var pizza in pizzas) {
print(pizza['name']);
}
}
Advanced usage #
Parsing search query strings #
You can parse search queries from strings. The supported syntax is very similar to other major search engines such as Lucene.
final query = Query.parse('New York Times date:>=2020-01-01');
Examples of supported queries:
Norwegian Forest cat
- Matches keywords "Norwegian", "Forest", and "cat".
"Norwegian Forest cat"
- A quoted keyword ensures that the words must appear as a sequence.
cat AND dog
- Matches keywords "cat" and "dog" (in any order).
cat OR dog
- Matches keyword "cat", "dog", or both.
pet -cat
- Matches keyword "pet", but excludes documents that match keyword "cat".
color:brown
- Color matches keyword "brown".
color:="brown"
- Color is equal to "brown".
weight:>=10
- Weight is greater than or equal to 10.
weight:[10 TO 20]
- Weight is between 10 and 20, inclusive.
weight:{10 TO 20}
- Weight is between 10 and 20, exclusive.
(cat OR dog) AND weight:>=10
- An example of grouping filters.
In equality/range expressions, the parser recognizes patterns such as:
- null, false, true, 3, 3.14
- 2020-12-31 (Date)
- 2020-12-31T00:00:00Z (DateTime)
- Other values are interpreted as strings