MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Comment décorer un élément de classe pour qu'il soit un index et obtenir la même chose qu'en utilisant EnsureIndex ?

Je pense que c'est une bonne idée, mais vous devez le faire vous-même, il n'y a pas de support intégré pour cela. Si vous avez une couche d'accès, vous pouvez le faire là-dedans. Vous auriez besoin d'une classe d'attributs, quelque chose comme ça ;

public enum IndexConstraints
{
    Normal     = 0x00000001, // Ascending, non-indexed
    Descending = 0x00000010,
    Unique     = 0x00000100,
    Sparse     = 0x00001000, // allows nulls in the indexed fields
}

// Applied to a member
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class EnsureIndexAttribute : EnsureIndexes
{
    public EnsureIndex(IndexConstraints ic = IndexConstraints.Normal) : base(ic) { }
}

// Applied to a class
[AttributeUsage(AttributeTargets.Class)]
public class EnsureIndexesAttribute : Attribute
{
    public bool Descending { get; private set; }
    public bool Unique { get; private set; }
    public bool Sparse { get; private set; }
    public string[] Keys { get; private set; }

    public EnsureIndexes(params string[] keys) : this(IndexConstraints.Normal, keys) {}
    public EnsureIndexes(IndexConstraints ic, params string[] keys)
    {
        this.Descending = ((ic & IndexConstraints.Descending) != 0);
        this.Unique = ((ic & IndexConstraints.Unique) != 0); ;
        this.Sparse = ((ic & IndexConstraints.Sparse) != 0); ;
        this.Keys = keys;
    }

}//class EnsureIndexes

Vous pouvez ensuite appliquer des attributs au niveau de la classe ou du membre comme suit. J'ai trouvé que l'ajout au niveau du membre était moins susceptible de se désynchroniser avec le schéma par rapport à l'ajout au niveau de la classe. Vous devez bien sûr vous assurer que vous obtenez le nom réel de l'élément par opposition au nom du membre C# ;

[CollectionName("People")]
//[EnsureIndexes("k")]// doing it here would allow for multi-key configs
public class Person 
{
    [BsonElement("k")] // name mapping in the DB schema
    [BsonIgnoreIfNull]
    [EnsureIndex(IndexConstraints.Unique|IndexConstraints.Sparse)] // name is implicit here
    public string userId{ get; protected set; }

// other properties go here
}

puis dans votre implémentation d'accès à la base de données (ou référentiel), vous avez besoin de quelque chose comme ça ;

    private void AssureIndexesNotInlinable()
    {
                // We can only index a collection if there's at least one element, otherwise it does nothing
                if (this.collection.Count() > 0)
                {

                    // Check for EnsureIndex Attribute
                    var theClass = typeof(T);

                    // Walk the members of the class to see if there are any directly attached index directives
                    foreach (var m in theClass.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
                    {
                        List<string> elementNameOverride = new List<string>(1);
                        EnsureIndexes indexAttr = null;

                        // For each members attribs
                        foreach (Attribute attr in m.GetCustomAttributes())
                        {
                            if (attr.GetType() == typeof(EnsureIndex))
                                indexAttr = (EnsureIndex)attr;

                            if (attr.GetType() == typeof(RepoElementAttribute))
                                elementNameOverride.Add(((RepoElementAttribute)attr).ElementName);

                            if ((indexAttr != null) && (elementNameOverride.Count != 0))
                                break;
                        }

                        // Index
                        if (indexAttr != null)
                        {
                            if (elementNameOverride.Count() > 0)
                                EnsureIndexesAsDeclared(indexAttr, elementNameOverride);
                            else
                                EnsureIndexesAsDeclared(indexAttr);
                        }
                    }

                    // Walk the atributes on the class itself. WARNING: We don't validate the member names here, we just create the indexes
                    // so if you create a unique index and don't have a field to match you'll get an exception as you try to add the second
                    // item with a null value on that key
                    foreach (Attribute attr in theClass.GetCustomAttributes(true))
                    {
                        if (attr.GetType() == typeof(EnsureIndexes))
                            EnsureIndexesAsDeclared((EnsureIndexes)attr);

                    }//foreach

                }//if this.collection.count

    }//AssureIndexesNotInlinable()

EnsureIndex ressemble alors à ceci ;

    private void EnsureIndexesAsDeclared(EnsureIndexes attr, List<string> indexFields = null)
    {
        var eia = attr as EnsureIndexes;

        if (indexFields == null)
            indexFields = eia.Keys.ToList();

        // use driver specific methods to actually create this index on the collection
        var db = GetRepositoryManager(); // if you have a repository or some other method of your own 
        db.EnsureIndexes(indexFields, attr.Descending, attr.Unique, attr.Sparse);

    }//EnsureIndexes()

Notez que vous le placerez après chaque mise à jour, car si vous oubliez quelque part, vos index risquent de ne pas être créés. Il est donc important de veiller à optimiser l'appel afin qu'il revienne rapidement s'il n'y a pas d'indexation à faire avant de parcourir tout ce code de réflexion. Idéalement, vous ne feriez cela qu'une seule fois, ou au moins une fois par démarrage de l'application. Donc, une façon serait d'utiliser un indicateur statique pour savoir si vous l'avez déjà fait, et vous auriez besoin d'une protection de verrouillage supplémentaire autour de cela, mais de manière trop simpliste, cela ressemble à ceci ;

    void AssureIndexes()
    {
        if (_requiresIndexing)
            AssureIndexesInit();
    }

Alors c'est la méthode que vous voudrez dans chaque mise à jour de base de données que vous effectuez, qui, si vous avez de la chance, sera également intégrée par l'optimiseur JIT.