Retrieving data

Retrieve and listen to changes to documents in your database.

Accessing your database

To access your database, you must first initialise an instance of Database. If you are accessing the default database linked to your project, you do not need to specify the database ID parameter.

var myDatabase = new Database(); // Initialising this project's database
var otherDatabase = new Database("databaseId") // Initialising another project's database

Referencing a document

To get a reference to a document in the database, simply define the path like how you would navigate to the document in the Dashboard.

Path parts and field names must not contain forward slashes ( / ), and cannot start with underscores ( _ ) or dollar signs ($). Additionally, path parts cannot contain spaces. The total length of the document path cannot exceed 230 characters.

var locationARef = database.Collection("locations").Document("A");

With the reference to the document, you can now perform operations on the document.

Retrieving a document

To retrieve a document, use the Get method on the document reference.

var locationADocument = await locationARef.Get();
Debug.Log($"Retrieved location document ID: {locationADocument.Id}");

Listening to changes to a document

Sphere Kit Database can invoke a method callback when the document is changed, so that you can update your UI accordingly. Use the Listen method on the document reference, and provide a callback for when an update is received, an error is received and the connection is closed. A function is returned, and when invoked, allows you to close this listener.

The callback for when an update is received, onData, includes a change parameter, which is an instance of SingleDocumentChange. When a document is initially retrieved in full or inserted, only change.Document is available, which is the full document. When a document is updated, you can also get the updated or deleted fields and their values using change.UpdatedFields and change.DeletedFields.

Specify the sendInitialData parameter to true to retrieve the full document immediately, before any changes. This helps you to set up your initial UI state.

By default, Sphere Kit Database intelligently reconnects to the listener when the internet connection drops, which is perfect for mobile games with varying signal strengths. However, if you would like to disable this functionality, just set the autoReconnect parameter to false.

var closeFn = locationARef.Listen(
    onData: change =>
    {
        switch (change.Type)
        {
            case SingleDocumentChangeType.Initial:
                /* When initial data is received, only change.Document is available. */
                Debug.Log("Initial data received.");
        
                break;
            case SingleDocumentChangeType.Insert:
                /* When an insert is received, only change.Document is available. */
                Debug.Log("Document inserted.");
        
                break;
            case SingleDocumentChangeType.Update:
                /* When an update is received, change.ChangeDescription and change.Document are available. */
                var updatedFields = change.ChangeDescription!.UpdatedFields;
                foreach (var field in updatedFields)
                {
                    Debug.Log($"Field updated: {field.Key} = {field.Value}");
                }
                /* Handle the updated field as needed */
                var deletedFields = change.ChangeDescription!.DeletedFields;
                foreach (var field in deletedFields)
                {
                    Debug.Log($"Field deleted: {field}");
                }
                /* Handle the deleted field as needed */
                Debug.Log("Document updated.");
        
                break;
        }
    },
    onError: error =>
    {
        Debug.LogError($"Error listening to documents: {error.Message}");
        /* Handle the error as needed */
    },
    onClosed: () =>
    {
        Debug.Log("Listener closed");
        /* Handle the listener being closed, e.g. by the game or due to network issues */
    },
    autoReconnect: true,
    sendInitialData: true
);

// ... later
await closeFn();

Referencing a collection

Path parts and field names must not contain forward slashes ( / ), and cannot start with underscores ( _ ) or dollar signs ($). Additionally, path parts cannot contain spaces. The total length of the document path cannot exceed 230 characters.

var locationsRef = database.Collection("locations");

With the reference to the collection, you can now perform operations on the collection.

Retrieving all documents in a collection

This method is more performant for retrieving all documents in a collection.

To retrieve all documents in a collection, use the GetDocuments method on the collection reference.

var locationsCollection = await locationsRef.GetDocuments();

foreach (var locationDocument in locationsCollection.Documents)
{
    Debug.Log("Location Document ID: " + locationDocument.Id);
}

Querying documents in a collection

Use this for including/excluding fields, sorting, limiting the number of documents retrieved and pagination as well.

To retrieve some documents based on a query, use the QueryDocuments method on the collection reference. Besides providing a list of queries, you can also include or exclude fields, sort the filtered documents and set a limit on the number of documents retrieved for pagination.

To sort items, you can specify a dictionary for the sort parameter with key as field name and value as either FieldSortDirection.Ascending or FieldSortDirection.Descending. To paginate, set startAfter to a dictionary with the same keys as sort and the respective values of the last document retrieved.

Defining your query operations

Operation name
Purpose
Example

And

Require all queries in the array of queries to be true to succeed.

DocumentQueryOperation.And( new [] {

// List of queries

} )

Nor

Require none of the queries in the array of queries to be true to succeed.

DocumentQueryOperation.Nor( new [] {

// List of queries

} )

Or

Require one of the queries in the array of queries to be true to succeed.

DocumentQueryOperation.Or( new [] {

// List of queries

} )

Equal

Checks if the field value is equal to the specified value.

DocumentQueryOperation.Equal ("rank", "gold")

NotEqual

Checks if the field value is not equal to the specified value.

DocumentQueryOperation.NotEqual ("rank", "bronze")

GreaterThan

Checks if the field value is greater than the specified value.

DocumentQueryOperation.GreaterThan ("skillLevel", 5)

GreaterThanOrEqual

Checks if the field value is greater than or equal to the specified value.

DocumentQueryOperation.GreaterThanOrEqual ("skillLevel", 5)

LessThan

Checks if the field value is less than the specified value.

DocumentQueryOperation.LessThan ("skillLevel", 10)

LessThanOrEqual

Checks if the field value is less than or equal to the specified value.

DocumentQueryOperation.LessThanOrEqual ("skillLevel", 10)

In

Checks if the value of the field is equal to any of the elements in the provided array.

DocumentQueryOperation.In ("rank", new [] { "silver", "gold" })

NotIn

Checks if the value of the field is not equal to any of the elements in the provided array.

DocumentQueryOperation.NotIn ("rank", new [] { "bronze" })

Exists

Checks if the field is set.

DocumentQueryOperation.Exists("group")

NotExists

Checks if the field is not set.

DocumentQueryOperation.NotExists("group")

DataTypeIs

Checks if the field data type is a certain data type.

DocumentQueryOperation.DataTypeIs ("rank", SphereKitDataType.String)

Modulo

Checks if the remainder of the field value divided by a divisor is equal to a certain value.

DocumentQueryOperation.Modulo ("gameLevel", 2, 0) // Checks if gameLevel is divisible by 2

MatchesRegex

Checks if the field value matches a regular expression pattern.

DocumentQueryOperation.MatchesRegex ("groupName", "^[a-zA-Z]+$")

GeoIntersects

Checks if the GeoJSON field intersects this GeoJSON object. Supported GeoJSON types are: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection.

DocumentQueryOperation.GeoIntersects(
  "availableArea", 
  new Dictionary<string, object>
  {
      { "type", "Point" },
      { "coordinates", new List<double> { 1, 3 } }
  }
);

GeoWithin

Checks if the GeoJSON field is within this GeoJSON object. Supported GeoJSON types are: Polygon, MultiPolygon.

DocumentQueryOperation.GeoWithin("treasureLocation", new Dictionary<string, object>
{
    { "type", "Polygon" },
    {
        "coordinates", new[]
        {
            new[]
            {
                new[] { 10.0, 20.0 },
                new[] { 10.0, 30.0 },
                new[] { 20.0, 30.0 },
                new[] { 20.0, 20.0 }
            }
        }
    }
});

GeoNear

Checks if the GeoJSON field is within a specified distance range from this GeoJSON object. Supported GeoJSON types are: Point.

DocumentQueryOperation.GeoNear("treasureLocation", new Dictionary<string, object>
    {
        { "type", "Point" },
        { "coordinates", new[] { 103.7749, 1.3338 } }
    },
    minDistance: 10,
    maxDistance: 100
);

ContainsAllOf

Check that the array field contains all the values specified.

DocumentQueryOperation.ContainsAllOf ("weapons", new[] { "sword", "shield" })

ElementMatches

Check that each element in the array field matches an array of subqueries. Specify null for the field parameter for subqueries where you would like to refer to the element being checked, and the subpath if you would like to check the inner fields of the element.

DocumentQueryOperation.ElementMatches("scores", new[]
{
    DocumentQueryOperation.GreaterThanOrEqual(null, 10),
    DocumentQueryOperation.LessThanOrEqual(null, 20)
});

ArraySizeIs

Checks if the array field has a certain number of elements.

DocumentQueryOperation.ArraySizeIs ("weapons", 3)

Example

var locationsCollection = await locationsRef.QueryDocuments(
    new[]
    {
        DocumentQueryOperation.GreaterThan("points", 10),
        DocumentQueryOperation.GeoNear("coordinates", myLocation, maxDistance: 100)
    },
    includeFields: new[] { "name", "points", "coordinates" },
    sort: new Dictionary<string, FieldSortDirection>
    {
        { "points", FieldSortDirection.Descending },
        { "name", FieldSortDirection.Ascending }
    },
    startAfter: new Dictionary<string, object>
    {
        { "points", 15 },
        { "name", "A" }
    },
    limit: 10
);

Listening to changes to multiple documents in a collection

Sphere Kit Database can invoke a method callback when documents in a collection are changed, so that you can update your UI accordingly. Use the ListenDocuments method on the collection reference, and provide a callback for when an update is received, an error is received and the connection is closed. A function is returned, and when invoked, allows you to close this listener.

The callback for when an update is received, onData, includes a change parameter, which is an instance of MultiDocumentChange. When the documents are initially retrieved in full, the change.Documents list is populated with the list of matching documents. When a document is inserted, updated or deleted, change.Documents will contain one element which is the document that was modified. When a document is updated, you can also get the updated or deleted fields and their values using change.ChangeDescription.UpdatedFields and change.ChangeDescription.DeletedFields.

You can also set a list of queries to filter the documents listened by the listener using the query parameter. See the docs above on defining your query operations.

Specify the sendInitialData parameter to true to retrieve a full list of matching documents immediately, before any changes. This helps you to set up your initial UI state.

By default, Sphere Kit Database intelligently reconnects to the listener when the internet connection drops, which is perfect for mobile games with varying signal strengths. However, if you would like to disable this functionality, just set the autoReconnect parameter to false.

var closeFn = locationsRef.ListenDocuments(
    onData: change =>
    {
        switch (change.Type)
        {
            case MultiDocumentChangeType.Initial:
                /* When initial data is received, only change.Documents is available. */
                Debug.Log("Initial data received:");
                foreach (var document in change.Documents)
                {
                    Debug.Log($"Document ID: {document.Reference.Id}");
                }
                /* Handle the initial documents as needed */

                break;
            case MultiDocumentChangeType.Insert:
                /* When an insert is received, change.Documents (with 1 element) is available. */
                var insertedDocument = change.Documents[0];
                Debug.Log($"Inserted document ID: {insertedDocument.Reference.Id}");
                /* Handle the inserted document as needed */

                break;
            case MultiDocumentChangeType.Update:
                /* When an update is received, change.ChangeDescription and change.Documents (with 1 element) is available. */
                var updatedFields = change.ChangeDescription!.UpdatedFields;
                foreach (var field in updatedFields)
                {
                    Debug.Log($"Field updated: {field.Key} = {field.Value}");
                    /* Handle the updated field as needed */
                }
                var deletedFields = change.ChangeDescription!.DeletedFields;
                foreach (var field in deletedFields)
                {
                    Debug.Log($"Field deleted: {field}");
                    /* Handle the deleted field as needed */
                }

                var changedDocument = change.Documents[0];
                Debug.Log($"Updated Document ID: {changedDocument.Reference.Id}");
                /* Handle the updated document as needed */

                break;
            case MultiDocumentChangeType.Delete:
                /* When a delete is received, only change.Documents (with 1 element) is available. */
                var deletedDocument = change.Documents[0];
                Debug.Log($"Deleted Document ID: {deletedDocument.Reference.Id}");
                /* Handle the deleted document as needed */

                break;
        }
    },
    onError: error =>
    {
        Debug.LogError($"Error listening to documents: {error.Message}");
        /* Handle the error as needed */
    },
    onClosed: () =>
    {
        Debug.Log("Listener closed");
        /* Handle the listener being closed, e.g. by the game or due to network issues */
    },
    query: new[]
    {
        DocumentQueryOperation.GreaterThan("points", 10),
        DocumentQueryOperation.GeoNear("coordinates", myLocation, maxDistance: 100)
    },
    includeFields: new[] { "name", "points", "coordinates" },
    sort: new Dictionary<string, FieldSortDirection>
    {
        { "points", FieldSortDirection.Descending },
        { "name", FieldSortDirection.Ascending }
    },
    autoReconnect: true,
    sendInitialData: true
);

// ... later
await closeFn();

Last updated