Voyage Data Persistence Techniques and Operations

Querying Objects by ID

When you know an object's _id value, you can instantiate it as an OID and use it in a query.

Person selectOne: {('_id' -> (OID fromValue: 16r55CDD2B6E9A87A520F000001))} asDictionary.

These two forms of OID creation are equivalent:

OID fromValue: 26555050698940995562836590593. "Decimal representation"
OID fromValue: 16r55CDD2B6E9A87A520F000001. "Hexadecimal representation"

If you have an instance from a root collection (a Person in this case), you can directly obtain its voyageId for queries. The following example assumes root collections named Trips and Persons, where a Trip contains an embedded collection of receipts, and a receipt has a description field. This query retrieves all trips for a specific person that contain at least one receipt with a given description.

Trip selectMany:
    {('receipts.description' -> targetString).
     ('person._id' -> targetPerson voyageId)} asDictionary

Unsupported MongoDB Commands via Voyage

Index Management

Creating and dropping indexes directly through Voyage is not currently supported. However, you can use OSProcess to execute MongoDB shell commands. Assuming a database named myDB with a Trips collection containing embedded receipts that have a description field, create an index on that field with:

OSProcess execute:
'/{mongoInstallPath}/bin/mongo --eval "db.getSiblingDB(''myDB'').Trips.createIndex({''receipts.description'':1})"'

To drop all indexes on the Trips collection:

OSProcess execute:
'/{mongoInstallPath}/bin/mongo --eval "db.getSiblingDB(''myDB'').Trips.dropIndexes()"'

Database Backup

Direct backup creation through Voyage is not available. Use the MongoDB mongodump tool:

OSProcess execute:
'/{mongoInstallPath}/bin/mongodump --out {BackupDirectory}'

Consult MongoDB documentation for command details, especially regarding the --eval option.

Helpful MongoDB Commands

Use .explain("executionStats") in the MongoDB shell to verify query execution and index usage.

Example after creating an index on an embedded field:

> db.Trips.createIndex({"receipts.description":1})
> db.Trips.find({"receipts.description":"a"}).explain("executionStats")
// Output shows index usage: "cursor" : "BtreeCursor receipts.description_1", "nscanned" : 2

After dropping indexes:

> db.Trips.dropIndexes()
> db.Trips.find({"receipts.description":"a"}).explain("executionStats")
// Output shows collection scan: "cursor" : "BasicCursor", "nscanned" : 246

Storing Date Instances in MongoDB

MongoDB does not natively distinguish between Smalltalk Date and DateAndTime types. Evenif you store a Date, queries will return a DateAndTime instance. You must explicitly convert it back to Date during object materialization.

Database Design Considerations

Object graphs often contain cycles rather than simple trees. For instance, a Person may reference Trips, and each Trip references its Person, creating a bidirectional relationship. Defining separate root collections for Persons and Trips prevents infinite serialization loops.

Example document structure showing references between root collections:

// Trip document referencing Person and PaymentMethod root collections
{
 "_id" : ObjectId("55cf2bc73c9b0fe702000008"),
 "person" : {
    "#collection" : "Persons",
    "_id" : ObjectId("55cf2bbb3c9b0fe702000007") },
 "receipts" : [
    { "paymentMethod" : {
        "#collection" : "PaymentMethods",
        "_id" : ObjectId("55cf2bbb3c9b0fe702000003") },
      "trip" : {
        "#collection" : "Trips",
        "_id" : ObjectId("55cf2bc73c9b0fe702000008") } } ]
}
// Corresponding Person document referencing Trips and Company
{
 "_id" : ObjectId("55cf2bbb3c9b0fe702000007"),
 "company" : {
    "#collection" : "Companies",
    "_id" : ObjectId("55cf2bbb3c9b0fe702000002") },
 "trips" : [
    { "#collection" : "Trips",
      "_id" : ObjectId("55cf2bc73c9b0fe702000008") } ]
}

For distinct business domains (e.g., customer management), consider creating separate repositories per domain.

Reading Existing MongoDB Data

You can retrieve data from MongoDB collections not originally created by Voyage. Define a class that acts as a Voyage root and describes the existing schema.

First, define the class as a Voyage root and specify its collection:

MyDataClass class >> isVoyageRoot
    ^ true

MyDataClass class >> voyageContainer
    <voyagecontainer>
    ^ VOContainer new
        collectionName: 'myExistingCollection';
        yourself</voyagecontainer>

Add instance variables and accessors matching the database fields. For a document like:

{ "_id" : ObjectId("5900a0175bc65a2b7973b48a"), "product" : "canvas", "quantity" : 100, "categories" : [ "cotton" ] }

Define Voyage descriptions for the fields:

MyDataClass class >> mongoProduct
    <mongodescription>
    ^ VOToOneDescription new
        attributeName: 'product';
        kind: String;
        yourself

MyDataClass class >> mongoQuantity
    <mongodescription>
    ^ VOToOneDescription new
        attributeName: 'quantity';
        kind: Integer;
        yourself

MyDataClass class >> mongoCategories
    <mongodescription>
    ^ VOToOneDescription new
        attributeName: 'categories';
        kind: OrderedCollection;
        yourself</mongodescription></mongodescription></mongodescription>

Connect to the database and retrieve data:

| repo |
repo := VOMongoRepository database: 'existingDatabase'.
repo selectAll: MyDataClass

Tags: Voyage mongodb Pharo Object-Persistence OODB

Posted on Wed, 20 May 2026 17:26:50 +0000 by Kazlaaz