Flutter atlas realm querying a collection using ObjectId contained in an embedded object from another collection failing

my schema.dart looks like this (simplified):

@RealmModel()
class _MainObj {
  @PrimaryKey()
  @MapTo('_id')
  late ObjectId id;
  @MapTo('name')
  late String name;
  @MapTo('description')
  late String? description;
  @MapTo('embedded_obj'')
  late List<_EmbeddedObj> embeddedObj;
}
@RealmModel(ObjectType.embeddedObject)
class _EmbeddedObj {
  @MapTo('embedded_obj2'')
  late _EmbeddedObj2 embeddedObj2;
  @MapTo('id_in_another_collection'')
  late ObjectId anotherCollectionId;
}

@RealmModel(ObjectType.embeddedObject)
class _EmbeddedObj2 {
  @MapTo('name')
  late String name;
}

@RealmModel()
class _AnotherCollection {
  @PrimaryKey()
  @MapTo('_id')
  late ObjectId id;
  @MapTo('name')
  late String name;
  @MapTo('embedded_obj2')
  late _EmbeddedObj2? embeddedObj2;
}

I have a top level StatefulWidget doing a realm.all for MainObj and doing a ListView.builder() on the results, which is working fine (based on the to_do flutter flexible template)…

  @override
  Widget build(BuildContext context) {
    final realmServices = Provider.of<RealmServices>(context);
    return Stack(
      children: [
        Column(
          children: [
            Expanded(
              child: Padding(
                padding: const EdgeInsets.fromLTRB(16, 0, 16, 0),
                child: StreamBuilder<RealmResultsChanges<MainObj>>(
                  stream: realmServices.realm.all<MainObj>().changes,
                  builder: (context, snapshot) {
                    final data = snapshot.data;

                    if (data == null) return waitingIndicator();

                    final results = data.results;
                    return ListView.builder(
                        shrinkWrap: true,
                        itemCount: results.realm.isClosed ? 0 : results.length,
                        itemBuilder: (context, index) => results[index].isValid
                            ? Container(
                                margin: const EdgeInsets.symmetric(
                                  horizontal: 12.0,
                                  vertical: 4.0,
                                ),
                                decoration: BoxDecoration(
                                  border: Border.all(),
                                  borderRadius: BorderRadius.circular(12.0),
                                  shape: BoxShape.rectangle,
                                ),
                                child: MainObjItem(results[index]))
                            : Container());
                  },
                ),
              ),
            ),
          ],
        ),
        realmServices.isWaiting ? waitingIndicator() : Container(),
      ],
    );
  }

and the ListView.builder invokes a Stateless Widget build (seems to be ok as well)…

class MainObjItem extends StatelessWidget {
  final MainObj _mainObj;
  const MainObjItem(this. _mainObj, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(_mainObj.name),
      subtitle: Text(
          _getAnotherCollectionName(context, _mainObj.embedded[0].anotherCollectionId)), // this checks out fine, there is always a property here
      onTap: () => {
        infoMessageSnackBar(context, "Not yet implemented").show(context),
      },
    );
  }

  String _getAnotherCollectionName(BuildContext context, ObjectId id) =>
      Provider.of<AnotherCollectionModel>(context, listen: false). getAnotherCollectionName(id);
}

and finally, a ChangeNotifier that queries the realm by id to find the document in the other collection…

class AnotherCollectionModel extends ChangeNotifier {
  Realm _realm;

  AnotherCollectionModel(this._realm);

  String getAnotherCollectionName(ObjectId id) {
    var queryStr = '_id == oid(${id.toString()})';
    var anotherCollection = _realm.query<AnotherCollection>(queryStr);
    return anotherCollection.length > 0 ? anotherCollection.name : "NOT FOUND";
  }
}

This, finally, is where my query is failing. I’ve confirmed the id coming in is correct, exists in the collection I’m querying, but both realm.query() and realm.find() don’t return any results for me.
I actually tested this last AnotherCollectionModel successfully using an id from an object that isn’t from an embedded object, it fails when the id is from an embedded object (To be precise, an id contained in an embedded object in a list held by the main object).

I’ve tried realm.query() with id as a parameterized query, ie

`realm.query( '_id == $0, [id])';`

along with the way I show above, and using realm.find(). None of them seem to work.
I’m not understanding why this fails, or a good way to troubleshoot it.

Thanks

Did you mean:

realm.query<AnotherCollection>( r'_id == $0', [id]);

?

Can you see the expected objects in the database with Realm Studio?

Also, are you using flexible sync? If so, how does your subscriptions look?

Hi Kasper,

I can see my objects in the realm using MongoDB Compass. I can confirm they exist in the collection, and the only rules in the database applying to the collections are readWriteAll (the top level default role).

I tried switching my query to your version, realm.query<AnotherCollection>( r'_id == $0', [id]);
and it didn’t work (I had tried this before). I didn’t have a query subscription to the AnotherCollection in my realm configuration. I went ahead and added it, but it’s not working for me, and I’m sure I have it wrong. Here’s what it currently looks like. I can’t seem to find any docs to help me set up a parameterized query subscription in flutter, not sure how it’s declared, it might be what I’m needing here…

I’m including my flexible sync configuration here, along with the subscriptions, but I’m pretty sure I’m not doing subscriptions for needed parameterized query correctly, and your help will get this all working for me.

Flexible Sync configuration:

RealmServices(this.app) {
    if (app.currentUser != null || currentUser != app.currentUser) {
      currentUser ??= app.currentUser;
      realm = Realm(Configuration.flexibleSync(
          currentUser!,
          [
            MainObj.schema,
            EmbeddedObj.schema,
            EmbeddedObj2.schema,
            AnotherCollection.schema,
          ],
          clientResetHandler: DiscardUnsyncedChangesHandler(
              onAfterReset: _onAfterReset,
              onBeforeReset: _onBeforeReset,
              onManualResetFallback: _onManualReset)));
      if (realm.subscriptions.isEmpty) {
        updateSubscriptions();
      }
    }

Subscriptions:

  Future<void> updateSubscriptions() async {
    if (realm.isClosed) {
      return;
    }
    realm.subscriptions.update((mutableSubscriptions) {
      mutableSubscriptions.clear();
      mutableSubscriptions.add(realm.all<MainObj>(), name: 'queryMainObj' );
      mutableSubscriptions.add(realm.query<AnotherCollection>(r'_id == $0', []),
          name: 'queryAnotherCollection);
    });
    await realm.subscriptions.waitForSynchronization();
  }

Thanks for the help!
Best,

Josh

I added a mutableSubscriptions.add(realm.all<AnotherCollection>(), 'anotherCollectionSubscription'); line to my subscriptions code, and it’s working now! Thanks for the help, it all got me pointed in the right direction!

Best, Josh

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.