TMS FNC Cloud Stellar Data Store Guides
TTMSFNCCloudStellarDataStore reads and writes structured records in a TMS
Stellar Data Store backend. The data model is three levels deep — a
project holds tables, a table holds metadata (its field definitions)
and entities (its records) — and every call is scoped by the ProjectID
and TableID you resolve after connecting. Reach for it when you want a
hosted table without running a database server yourself; reach for a REST
client instead when the backend is not Stellar. This page covers connecting
and scoping, inserting single records and batches, and querying with filters,
sorting, field selection and de-duplication.
Authentication and connecting
Set the OAuth credentials of your registered app on Authentication, then
call Connect. OnConnected fires once the session is usable — resolve the
project and table from there, because ProjectID and TableID scope every
later request and a call made without them raises an exception rather than
failing silently. GetProjects reports through OnGetProjects, GetTables
through OnGetTables, and TableByName resolves a table from a listing.
procedure TForm1.FormCreate(Sender: TObject);
begin
TMSFNCCloudStellarDataStore1.Authentication.ClientID := 'your-app-client-id';
TMSFNCCloudStellarDataStore1.Authentication.Secret := 'your-app-secret';
TMSFNCCloudStellarDataStore1.Authentication.CallBackURL := 'http://127.0.0.1:8000';
TMSFNCCloudStellarDataStore1.OnConnected := StoreConnected;
TMSFNCCloudStellarDataStore1.OnGetProjects := StoreProjectsListed;
TMSFNCCloudStellarDataStore1.OnGetTables := StoreTablesListed;
TMSFNCCloudStellarDataStore1.Connect;
end;
procedure TForm1.StoreConnected(Sender: TObject);
begin
// ProjectID and TableID scope every later call, so resolve them first.
TMSFNCCloudStellarDataStore1.GetProjects;
end;
procedure TForm1.StoreProjectsListed(Sender: TObject;
const AInfo: TTMSFNCCloudStellarDataStoreProjects;
const ARequestResult: TTMSFNCCloudBaseRequestResult);
begin
if not ARequestResult.Success or (AInfo.Count = 0) then
Exit;
TMSFNCCloudStellarDataStore1.ProjectId := AInfo[0].ID;
TMSFNCCloudStellarDataStore1.GetTables;
end;
procedure TForm1.StoreTablesListed(Sender: TObject;
const AInfo: TTMSFNCCloudStellarDataStoreTables;
const ARequestResult: TTMSFNCCloudBaseRequestResult);
var
Table: TTMSFNCCloudStellarDataStoreTable;
begin
if not ARequestResult.Success then
Exit;
Table := TMSFNCCloudStellarDataStore1.TableByName('Contacts');
if Assigned(Table) then
begin
TMSFNCCloudStellarDataStore1.TableId := Table.ID;
TMSFNCCloudStellarDataStore1.GetMetaData;
end;
end;
Requests are asynchronous by default. Setting Mode to moSync makes each
call block until the service answers, which is what the setup path above
would use in a batch or startup routine; switch it back to moAsync for
UI-driven work.
Inserting records
Insert is overloaded, and which overload you call decides which event
reports the outcome:
| Overload | Inserts | Reports through |
|---|---|---|
Insert(AValues: TStringList) |
one record from name/value pairs | OnInsert |
Insert(AEntity: TTMSFNCCloudStellarDataStoreEntity) |
one entity | OnInsert |
Insert(AEntities: TTMSFNCCloudStellarDataStoreEntities) |
every entity in the collection, in one request | OnInsertAll |
Build records by adding to the Entities collection and assigning through
Values['<field>'], which accepts any TValue — string, integer, float,
boolean or TDateTime — as long as the field exists in the table metadata.
Batch inserts and OnInsertAll
Use the collection overload when importing more than a handful of records:
one HTTP round trip instead of one per record, which matters as much for rate
limits as for speed. OnInsertAll receives the whole inserted set with the
IDs the service assigned, so it is where you refresh a list or map the new
IDs back onto your local data. Note that OnInsert does not fire for a
batch — wire the event that matches your overload.
procedure TForm1.ImportContacts;
var
Entity: TTMSFNCCloudStellarDataStoreEntity;
begin
// OnInsert reports a single-record insert; OnInsertAll reports a batch, so
// wire the one that matches the overload you call.
TMSFNCCloudStellarDataStore1.OnInsertAll := StoreRecordsInserted;
Entity := TMSFNCCloudStellarDataStore1.Entities.Add;
Entity.Values['FirstName'] := 'Isaac';
Entity.Values['LastName'] := 'Tailor';
Entity.Values['Email'] := 'isaac@contacts.com';
Entity.Values['Admin'] := True;
Entity := TMSFNCCloudStellarDataStore1.Entities.Add;
Entity.Values['FirstName'] := 'Laura';
Entity.Values['LastName'] := 'Schneider';
Entity.Values['Email'] := 'laura@contacts.com';
Entity.Values['Admin'] := False;
// One request for the whole collection instead of one request per record.
TMSFNCCloudStellarDataStore1.Insert(TMSFNCCloudStellarDataStore1.Entities);
end;
procedure TForm1.StoreRecordsInserted(Sender: TObject;
const AInfo: TTMSFNCCloudStellarDataStoreEntities;
const ARequestResult: TTMSFNCCloudBaseRequestResult);
var
I: Integer;
begin
if not ARequestResult.Success then
begin
ShowMessage('Batch insert failed: ' + ARequestResult.ResultString);
Exit;
end;
// AInfo carries the inserted entities with the IDs the service assigned.
for I := 0 to AInfo.Count - 1 do
ListBox1.Items.Add(AInfo[I].ValueAsString['FirstName'] + ' #' + AInfo[I].ID);
end;
Querying
Query reports its result through OnQuery, which receives the matching
entities. Read a field with ValueAsString['<field>'] for display, or
Values['<field>'] for the typed value.
Important
A where-clause, sort order, field selection or Distinct flag is only sent
when it is passed into the query. Table.Query forwards the table's own
Filters, SortOrder, JoinQuery, SelectQuery and Distinct, and the
Query(AFilters, ASortOrder, AJoinQuery, ASelectQuery, ADistinct) overload
takes them as arguments. The parameterless Query and the
Query(APageSize, APageIndex) overload send none of them — so setting the
component's own Filter, SelectQuery or Distinct property and then
calling Query returns an unfiltered result. Configure the query on the
table, and run it with Table.Query.
Filter operators
A filter is a field, a value, a comparison operator
(TTMSFNCCloudComparisonOperator) and a logical operator
(TTMSFNCCloudLogicalOperator, loAnd / loOr / loNone) that joins it to
the preceding filter. Beyond the basic coEqual, coLike, coLarger,
coSmaller and coContains, the operator set covers inclusive range bounds,
negation and null tests:
| Operator | Matches |
|---|---|
coEqual / coNotEqual |
field equals / does not equal the value |
coLarger / coSmaller |
strictly greater / less than the value |
coLargerThanOrEqual / coSmallerThanOrEqual |
greater / less than or equal to the value — use these for inclusive range bounds |
coLike / coNotLike |
pattern matches / does not match the value |
coContains |
the value is one of a set of values |
coNull |
the field has no value stored |
procedure TForm1.QueryActiveTopAccounts;
var
Table: TTMSFNCCloudStellarDataStoreTable;
begin
TMSFNCCloudStellarDataStore1.OnQuery := StoreQueried;
Table := TMSFNCCloudStellarDataStore1.TableByName('Contacts');
if not Assigned(Table) then
Exit;
Table.Filters.Clear;
// Range bounds: coLargerThanOrEqual / coSmallerThanOrEqual include the
// boundary value, where coLarger / coSmaller exclude it.
Table.Filters.Add('Sales', 1000, coLargerThanOrEqual, loAnd);
Table.Filters.Add('Sales', 50000, coSmallerThanOrEqual, loAnd);
// coNotEqual excludes a single value; coNotLike negates a pattern match.
Table.Filters.Add('Country', 'XX', coNotEqual, loAnd);
Table.Filters.Add('Email', '%@example.com', coNotLike, loAnd);
// coNull matches records where the field was never filled in.
Table.Filters.Add('ClosedOn', '', coNull, loAnd);
Table.Query;
end;
Selecting fields and removing duplicates
SelectQuery restricts a query to a comma-separated list of fields instead
of the whole record, which is the cheapest way to populate a lookup list or a
single-column picker. Distinct collapses duplicate rows of that selection —
it is only sent when SelectQuery is non-empty, so Distinct on its own has
no effect.
procedure TForm1.ListCountries;
var
Table: TTMSFNCCloudStellarDataStoreTable;
begin
TMSFNCCloudStellarDataStore1.OnQuery := StoreQueried;
Table := TMSFNCCloudStellarDataStore1.TableByName('Contacts');
if not Assigned(Table) then
Exit;
// SelectQuery narrows the request to the listed fields instead of every
// column, which keeps the payload small.
Table.SelectQuery := 'Country';
// Distinct only takes effect together with a SelectQuery - it collapses
// duplicate rows of the selected fields.
Table.Distinct := True;
// Table.Query forwards the table's own SelectQuery, Distinct, Filters,
// SortOrder and JoinQuery; the parameterless DataStore.Query does not.
Table.Query;
end;
procedure TForm1.StoreQueried(Sender: TObject;
const AInfo: TTMSFNCCloudStellarDataStoreEntities;
const ARequestResult: TTMSFNCCloudBaseRequestResult);
var
I: Integer;
begin
if not ARequestResult.Success then
Exit;
ListBox1.Items.Clear;
for I := 0 to AInfo.Count - 1 do
ListBox1.Items.Add(AInfo[I].ValueAsString['Country']);
end;
Combining filters, sorting, selection and paging
The query parts compose: the same table can carry a filter set, a sort order,
a field selection, the Distinct flag and a page window, and Table.Query
sends them together.
procedure TForm1.QueryDistinctActiveCountries;
var
Table: TTMSFNCCloudStellarDataStoreTable;
begin
TMSFNCCloudStellarDataStore1.OnQuery := StoreQueried;
Table := TMSFNCCloudStellarDataStore1.TableByName('Contacts');
if not Assigned(Table) then
Exit;
// Filters, SortOrder, SelectQuery and Distinct all travel together on the
// table, so one Table.Query call sends the complete where/sort/select
// clause set.
Table.Filters.Clear;
Table.Filters.Add('Sales', 1000, coLargerThanOrEqual, loAnd);
Table.Filters.Add('ClosedOn', '', coNull, loAnd);
Table.SortOrder.Clear;
Table.SortOrder.Add('Country', soAscending);
Table.SelectQuery := 'Country';
Table.Distinct := True;
// Page size and index are optional; pass them to cap a large result set.
Table.Query(50, 0);
end;
Common mistakes
- Calling the parameterless
Queryafter configuring filters. It sends no where/sort/select clause at all. UseTable.Query, or the fullQueryoverload. - Setting
SelectQueryorFilteron the component. Those properties are stored but never read by a query. The table-levelSelectQuery,DistinctandFiltersare the ones that reach the service. - Setting
DistinctwithoutSelectQuery. The flag is only appended alongside a field selection. - Handling
OnInsertfor a batch insert. The collection overload reports throughOnInsertAllonly. - Querying before
ProjectIDandTableIDare resolved. A missing project or table ID raises an exception; resolve both fromOnConnected/OnGetProjects/OnGetTablesfirst. - Assigning a value for a field that is not in the metadata. Add the
field with
AddMetaDatabefore inserting records that use it.
See also
- TTMSFNCCloudStellarDataStore — full class reference
- Get started
- Release notes