Litedb: Query DbRef without use Include

Created on 2 Oct 2017  路  6Comments  路  Source: mbdavid/LiteDB

Hi,

I use a base class with generic methods to get data from collections and the children classes have different properties using DbRef attribute.

There is any possibility to query DbRef fields without need to Include each of them?

Thanks.

All 6 comments

Hi @sandolkakos, are you want query data from a collection using external document (from another collection, used DbRef)? It's not possible (in LiteDB), not even using Include. You can only using LinqToObject.

It's because filter data if possible only if data exists inside the document are you testing. There is no JOIN like relation database. If you need filter a document, all data must be inside in this document (you can use sub-document to that).

I understand. Thanks @mbdavid
Meanwhile, I'm removing the DbRef and storing the data on the same document.
Only to show my generic implementation, case somebody want to know how I did it.

Creating a custom class Attribute to name collections directly on classes.

[AttributeUsage(AttributeTargets.Class)]
public class CollectionNameAttribute : Attribute
{
    public string Name { get; set; }

    public CollectionNameAttribute(string name)
    {
        Name = name;
    }
}

Two simple classes:

[CollectionName("users")]
public class User : BaseModel<User>
{
    [BsonField("name")]
    public string Name { get; set; }

    [BsonField("age")]
    public int Age { get; set; }
}

public class UserMatch : BaseModel<UserMatch>
{
    [BsonField("user")]
    public User User { get; set; }

    [BsonField("score")]
    public int Score { get; set; }
}

The BaseModel with:

  • Id property to be shared with all children classes.
  • CollectionName method to return the name configured directly on class OR the name of class in plural.
  • Generic methods to Load/Save data.
public class BaseModel<T> where T : new()
{
    [BsonIndex(true)]
    public int Id { get; set; }

    private const string DATABASE_PATH = @"C:\Temp\MyData.db";

    public static string CollectionName()
    {
        var tableAttr = (CollectionNameAttribute)typeof(T).GetCustomAttributes(typeof(CollectionNameAttribute), true).FirstOrDefault();
        if (tableAttr != null)
        {
            return tableAttr.Name;
        }

        string name = typeof(T).Name.ToLower();
        string endName = name.Substring(name.Length-3);

        if (name.EndsWith("y"))
        {
            name = name.Substring(0, name.Length - 1) + "ies";
        }
        else if (name.EndsWith("ch") || name.EndsWith("x") || name.EndsWith("ss"))
        {
            name = name + "es";
        }
        else if (!name.EndsWith("s"))
        {
            name = name + "s";
        }

        return name;
    }

    public static List<T> GetAll()
    {
        using (var db = new LiteDatabase(DATABASE_PATH))
        {
            LiteCollection<T> collection = db.GetCollection<T>(CollectionName());

            List<T> result = collection.FindAll().ToList();

            return result;
        }
    }

    public static List<T> GetAll(System.Func<T, bool> predicate)
    {
        using (var db = new LiteDatabase(DATABASE_PATH))
        {
            LiteCollection<T> collection = db.GetCollection<T>(CollectionName());

            List<T> result = pathsCollection.FindAll().Where(predicate).ToList();

            return result;
        }
    }

    public static T GetOne(System.Func<T, bool> predicate)
    {
        using (var db = new LiteDatabase(DATABASE_PATH))
        {
            LiteCollection<T> collection = db.GetCollection<T>(CollectionName());

            T result = pathsCollection.FindAll().Where(predicate).First();

            return result;
        }
    }

    public void Save()
    {
        using (var db = new LiteDatabase(DATABASE_PATH))
        {
            LiteCollection<T> collection = db.GetCollection<T>(CollectionName());

            collection.Upsert((T)(object)this);
        }
    }

    public void SaveList(List<T> list)
    {
        using (var db = new LiteDatabase(DATABASE_PATH))
        {
            LiteCollection<T> collection = db.GetCollection<T>(CollectionName());

            collection.Upsert(list);
        }
    }
}

Hi @sandolkakos, I recommend you to use Find and Expression do use LiteDB index query. In you example, you always run a "full search" and will filter document after read all from database.

Thanks for the attention @mbdavid.

Yes, I agree with you. But at the first time I used the Expression directly in the Find, I found an error to execute complexes expressions. So I ended up changing my generic methods to execute a FindAll and later execute complexes expressions using Where. If you have any tips about this, it will be very welcome :)

PS.: I'm using LiteDB in Unity (dll from unity3d branch).

Here is the old method:

public static List<T> GetAll(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
    using (var db = new LiteDatabase(DATABASE_PATH))
    {
        LiteCollection<T> collection = db.GetCollection<T>(CollectionName());

        List<T> result = collection.Find(predicate).ToList();

        return result;
    }
}

This simple example does not work on LiteDB default Expression:

List<Word> result = Word.GetAll(x => x.Name.ToLower() == "avestruz");

Error returned:

LiteException: Property 'Name.ToLower(' was not mapped into BsonDocument.
LiteDB.QueryVisitor`1[AlfaEBetoSolucoes.AprenderALer.Models.Contents.Word].GetField (System.Linq.Expressions.Expression expr, System.String prefix)
LiteDB.QueryVisitor`1[AlfaEBetoSolucoes.AprenderALer.Models.Contents.Word].VisitExpression (System.Linq.Expressions.Expression expr, System.String prefix)
LiteDB.QueryVisitor`1[AlfaEBetoSolucoes.AprenderALer.Models.Contents.Word].Visit (System.Linq.Expressions.Expression`1 predicate)
LiteDB.LiteCollection`1[AlfaEBetoSolucoes.AprenderALer.Models.Contents.Word].Find (System.Linq.Expressions.Expression`1 predicate, Int32 skip, Int32 limit)
Teste.Teste1 () (at Assets/zz_LocalTests/Teste.cs:36)

Hi, if you are using in Unity, ok, because there are some "non-working" thing in v4 that doesnt work in Unity. But, I still recommend you than create 2 methods: one for use index and other for full scan.

Hi! With the objective of organizing our issues, we are closing old unsolved issues. Please check the latest version of LiteDB and open a new issue if your problem/question/suggestion still applies. Thanks!

Was this page helpful?
0 / 5 - 0 ratings