Laravel & MongoDB – relationships with ObjectId

I’ve used Laravel and jenssegers/laravel-mongodb package for a long time. It’s wonderful thing, because it allows to use MongoDB easily, integrate most of things with Eloquent and Laravel Models, but also offers hybrid relationships with different database drivers. In theory it supports all relationships in MongoDB database, but in practise, there are a lot of potential issues because of current implementation. 

On MonogDB you can use many fields to create relations and use them in $lookups (aggregations) after that, but the most common practise is to use ObjectId – it’s default field type for keys (_id) and of course you can use different, but probably you will use that one. The problem is jenssegers package uses string for all relationships – and it works (but not always), until you want to use custom aggregations and lookups. These is an example of our parent model with some child relationship:

use Jenssegers\Mongodb\Relations\HasMany;

class ParentModel 
{
    public function children(): HasMany
    {
        return $this->hasMany(ChildModel::class, 'parent_model_id');
    }

}

And then you want to add some children to parent model instance:

$child = new ChildModel();
$parentModel->children()->save($child);

What will be the effect? Child record will have parent_model_id field of course, but it will be string field, not ObjectId. It’s fine until you will need to use $lookup – in that case, simple join will NOT work, because you have ObjectId key (_id) in Parent collection and string relation field on Child collection. We will need to prepare additional field using $addFields in Parrent collection before $lookup stage or in Child collection during $lookup pipeline. It’s not efficient way to solve that problem.

So, how to handle with that? Solution is easy and already available on new package development version: skip automatic casting key to string. Unfortunately, looks like it is not maintained actively, and we will wait a bit longer for updated version. But we can add required change right now. Just overwrite ParentModel getIdAttribute to fix that and always return clean value, without any modification:

class ParentModel
{
    public function getIdAttribute($value = null)
    {
        return $value;
    }
}

After that change code to add child:

$parentModel->children()->save($child);

will not use string in parent_model_id field anymore. It will be ObjectId and everything will work correctly: build-in Laravel relationships and also $lookups, without any additional fields.

Any drawbacks? Yes, after such change, you need to remember to cast key if you want to send it to client as a string or use in comparisons:

$parentModel->getKey() === (string)$parentModel->getKey() 
// before change: true
// after change: false, because left is ObjectId instance

// JSON resource:

return [
    'id' => (string) $parentModel->getKey(),

    // or
    'id' => $parentModel->getKey()->toString(),
]

But I think it’s not a big deal – it’s better to always have one type instead of many different in the same field. 

MongoDB – Natural sort

One of the most common problems with databases are sorting. Let’s see an simple example: we use MySQL/MariaDB and have table with some data. We want to sort this data. Everything is ok if we use only values in the same type in field, for example numeric (maybe some amount?), strings (titles), dates, keys etc. But what if field is custom, user-typed data and may contain many types? In that case, we have big problem, because standard sorting is not enought, it will not work. Example, value: “11”, “1”, “2”, “3”, “2a” will be sorted in order: 1, 11, 2, 2a, 3, not as we expected i.e. 1, 2, 2a, 3, 11. The solution is to use natural sorting.

Continue reading “MongoDB – Natural sort”

Laravel Eloquent: MongoDB relations

Laravel offers very complex Eloquent – ORM simple to use, but with a lot of options. One of them is relations support. We can define several types of relations between our models and then use them to simplify get the data. Relations are also support by MongoDB extension for Eloquent, which is available on GitHub. Sometimes it may be difficult to use because of some issues with ObjectId and BSON. Let’s see how it can be solved.

Continue reading “Laravel Eloquent: MongoDB relations”

GROUP BY ($group) in MongoDB

Mongodb is not relational database, but we sometimes want to use functions similar from such databases. I wrote about JOINs in Mongo few weeks ago, but it isn’t only one option. Second and very interesting is grouping – something like GROUP BY in for example MySQL database. Do we really need grouping in Mongo? Maybe yes, maybe no, but I must implement such option for one of application modules. It isn’t difficult and we can do this very simply. Again, I will use examples from PHP and mongodb extension for this language – code should be very similar in other languages.

Continue reading “GROUP BY ($group) in MongoDB”

PHP/Mongo – Using JOIN in queries

I’ve written one post about MongoDB and PHP about creating nested set model in this database, but there is another interesting topic: using JOINs in mongo queries. Sorry, wh… what? Joining tables in non-relational database? Yes, it’s possible and we can do this to collect much more data in only one, but more complicated query. Let’s start to see how we can achieve this.

Continue reading “PHP/Mongo – Using JOIN in queries”