Read the vault structure
Most calls take IDs: object type IDs, class IDs, property definition IDs, value list IDs. Apart from the built-in ones, these IDs differ from vault to vault. Read the structure once, cache it, and look up IDs by alias or name instead of hard-coding them.
Calls
| What | Call | Response field |
|---|---|---|
| Object types | GetObjectTypes | object_types: the real object types, not the value lists |
| One object type | GetObjectType | putf8szobjtypebasic |
| Property definitions | GetPropertyDefs | property_defs |
| Classes and class groups | GetObjectClassesAndGroups | classes, class_groups |
| ID by alias | GetMetadataStructureItemIdByAlias | id; -1 (-1000 for named ACLs) if no item has the alias |
- A class (ObjectClass) has its ID and name in
base_info.item_info(obj_id.item_id.internal_id,name), its object type inobject_type, its properties inassociated_property_defs(withis_required) and its default workflow indefault_workflow(0 for none). - A property definition (PropertyDef) has
data_type, for lookups the value list it refers to invalue_list, andis_predefinedtrue for the built-in ones. - Cache the structure and read it again when the vault's metadata structure version changes; see Metadata structure version and caching.
Code
The recipe lists the object types, counts the property definitions, and lists the document
classes with the properties each requires apart from the built-in ones. Then it resolves a class
alias and a property alias (defaults MF.CL.Document and MF.PD.Keywords; pass others as
arguments).
Python
# Object types. GetObjectTypes returns the real object types, not the value lists.
object_types = client.stub(IRPCObjectTypes_pb2_grpc.IRPCObjectTypesStub)
types = object_types.GetObjectTypes(
IRPCObjectTypes_pb2.GetObjectTypesRequest()).object_types
print(f"Object types: {len(types)}")
for object_type in types:
print(f" {object_type.id} {object_type.name_plural}")
# Property definitions and classes.
property_defs = client.stub(IRPCPropertyDefs_pb2_grpc.IRPCPropertyDefsStub)
properties = {p.id: p for p in property_defs.GetPropertyDefs(
IRPCPropertyDefs_pb2.GetPropertyDefsRequest()).property_defs}
print(f"Property definitions: {len(properties)}")
classes = property_defs.GetObjectClassesAndGroups(
IRPCPropertyDefs_pb2.GetObjectClassesAndGroupsRequest()).classes
document_classes = [c for c in classes if c.object_type == 0]
print(f"Document classes: {len(document_classes)}")
for object_class in document_classes:
info = object_class.base_info.item_info
# The properties a new object of the class must have, other than built-in ones.
required = [properties[p.property_def].name
for p in object_class.associated_property_defs
if p.is_required and p.property_def in properties
and not properties[p.property_def].is_predefined]
print(f" {info.obj_id.item_id.internal_id} {info.name}; "
f"required: {', '.join(required) or '-'}")
Source: python/recipes/vault_structure.py
# Code that must work in many vaults refers to classes and properties by alias, not ID.
# The answer is -1 when no item, or more than one, has the alias.
vault = client.stub(IRPCVault_pb2_grpc.IRPCVaultStub)
class_id = vault.GetMetadataStructureItemIdByAlias(
IRPCVault_pb2.GetMetadataStructureItemIdByAliasRequest(
item_type=enums.METADATA_STRUCTURE_ITEM_OBJECT_CLASS, alias=class_alias)).id
property_id = vault.GetMetadataStructureItemIdByAlias(
IRPCVault_pb2.GetMetadataStructureItemIdByAliasRequest(
item_type=enums.METADATA_STRUCTURE_ITEM_PROPERTY_DEF, alias=property_alias)).id
print(f"Class alias {class_alias}: ID {class_id}")
print(f"Property alias {property_alias}: ID {property_id}")
Source: python/recipes/vault_structure.py
C#
// Object types. GetObjectTypes returns the real object types, not the value lists.
var objectTypes = new IRPCObjectTypes.IRPCObjectTypesClient(connection.CallInvoker);
var types = (await objectTypes.GetObjectTypesAsync(new GetObjectTypesRequest())).ObjectTypes;
Console.WriteLine($"Object types: {types.Count}");
foreach (var type in types)
Console.WriteLine($" {type.Id} {type.NamePlural}");
// Property definitions and classes.
var propertyDefs = new IRPCPropertyDefs.IRPCPropertyDefsClient(connection.CallInvoker);
var properties = (await propertyDefs.GetPropertyDefsAsync(new GetPropertyDefsRequest()))
.PropertyDefs.ToDictionary(p => p.Id);
Console.WriteLine($"Property definitions: {properties.Count}");
var classes = (await propertyDefs.GetObjectClassesAndGroupsAsync(new GetObjectClassesAndGroupsRequest()))
.Classes;
var documentClasses = classes.Where(c => c.ObjectType == 0).ToList();
Console.WriteLine($"Document classes: {documentClasses.Count}");
foreach (var objectClass in documentClasses)
{
var info = objectClass.BaseInfo.ItemInfo;
// The properties a new object of the class must have, other than built-in ones.
var required = objectClass.AssociatedPropertyDefs
.Where(p => p.IsRequired)
.Select(p => properties.GetValueOrDefault(p.PropertyDef))
.Where(def => def is { IsPredefined: false })
.Select(def => def!.Name);
Console.WriteLine($" {info.ObjId.ItemId.InternalId} {info.Name}; " +
$"required: {string.Join(", ", required.DefaultIfEmpty("-"))}");
}
Source: csharp/Recipes/VaultStructure.cs
// Code that must work in many vaults refers to classes and properties by alias, not ID.
// The answer is -1 when no item, or more than one, has the alias.
var vault = new IRPCVault.IRPCVaultClient(connection.CallInvoker);
var classId = await vault.GetMetadataStructureItemIdByAliasAsync(
new GetMetadataStructureItemIdByAliasRequest
{
ItemType = MetadataStructureItem.ObjectClass,
Alias = classAlias,
});
var propertyId = await vault.GetMetadataStructureItemIdByAliasAsync(
new GetMetadataStructureItemIdByAliasRequest
{
ItemType = MetadataStructureItem.PropertyDef,
Alias = propertyAlias,
});
Console.WriteLine($"Class alias {classAlias}: ID {classId.Id}");
Console.WriteLine($"Property alias {propertyAlias}: ID {propertyId.Id}");
Source: csharp/Recipes/VaultStructure.cs
Go
// Object types. GetObjectTypes returns the real object types, not the value lists.
types, err := mfiles.NewIRPCObjectTypesClient(client.Conn).GetObjectTypes(ctx,
&mfiles.GetObjectTypesRequest{})
if err != nil {
return err
}
fmt.Printf("Object types: %d\n", len(types.GetObjectTypes()))
for _, objectType := range types.GetObjectTypes() {
fmt.Printf(" %d %s\n", objectType.GetId(), objectType.GetNamePlural())
}
// Property definitions and classes.
propertyDefs := mfiles.NewIRPCPropertyDefsClient(client.Conn)
defs, err := propertyDefs.GetPropertyDefs(ctx, &mfiles.GetPropertyDefsRequest{})
if err != nil {
return err
}
properties := map[int32]*mfiles.PropertyDef{}
for _, def := range defs.GetPropertyDefs() {
properties[def.GetId()] = def
}
fmt.Printf("Property definitions: %d\n", len(properties))
classes, err := propertyDefs.GetObjectClassesAndGroups(ctx,
&mfiles.GetObjectClassesAndGroupsRequest{})
if err != nil {
return err
}
var documentClasses []*mfiles.ObjectClass
for _, class := range classes.GetClasses() {
if class.GetObjectType() == 0 {
documentClasses = append(documentClasses, class)
}
}
fmt.Printf("Document classes: %d\n", len(documentClasses))
for _, class := range documentClasses {
info := class.GetBaseInfo().GetItemInfo()
// The properties a new object of the class must have, other than built-in ones.
var required []string
for _, associated := range class.GetAssociatedPropertyDefs() {
def, ok := properties[associated.GetPropertyDef()]
if associated.GetIsRequired() && ok && !def.GetIsPredefined() {
required = append(required, def.GetName())
}
}
if len(required) == 0 {
required = []string{"-"}
}
fmt.Printf(" %d %s; required: %s\n", info.GetObjId().GetItemId().GetInternalId(),
info.GetName(), strings.Join(required, ", "))
}
Source: go/recipes/vault_structure/main.go
// Code that must work in many vaults refers to classes and properties by alias, not ID.
// The answer is -1 when no item, or more than one, has the alias.
vault := mfiles.NewIRPCVaultClient(client.Conn)
class, err := vault.GetMetadataStructureItemIdByAlias(ctx,
&mfiles.GetMetadataStructureItemIdByAliasRequest{
ItemType: mfiles.MetadataStructureItem_METADATA_STRUCTURE_ITEM_OBJECT_CLASS,
Alias: classAlias,
})
if err != nil {
return err
}
property, err := vault.GetMetadataStructureItemIdByAlias(ctx,
&mfiles.GetMetadataStructureItemIdByAliasRequest{
ItemType: mfiles.MetadataStructureItem_METADATA_STRUCTURE_ITEM_PROPERTY_DEF,
Alias: propertyAlias,
})
if err != nil {
return err
}
fmt.Printf("Class alias %s: ID %d\n", classAlias, class.GetId())
fmt.Printf("Property alias %s: ID %d\n", propertyAlias, property.GetId())
Source: go/recipes/vault_structure/main.go
Output for a normal user of the M-Files Sample Vault, which defines no aliases:
Object types: 11
0 Documents
9 Document collections
10 Assignments
15
19 Shares
...
156 Employees
Property definitions: 143
Document classes: 31
0 Document; required: -
1 Other Document; required: -
2 Purchase Invoice; required: Document date
4 Contract or Agreement; required: Agreement title, Agreement type, Owner, Document date
5 Proposal; required: Proposal title, Document date, Proposal number
...
Class alias MF.CL.Document: ID -1
Property alias MF.PD.Keywords: ID -1
Pitfalls
- An unknown alias is not an error.
GetMetadataStructureItemIdByAliasreturns -1, or -1000 for a named ACL (-1 is a real named ACL); check for it before using the ID. An empty alias fails with "The parameter is incorrect.". item_typeselects the ID space: an alias of a property definition is looked up withMETADATA_STRUCTURE_ITEM_PROPERTY_DEF, of a class withMETADATA_STRUCTURE_ITEM_OBJECT_CLASS.- To list all aliases, GetMetadataStructureItemIdsByAliases
and GetAllMetadataStructureItemIdswithAliases
need
unambiguous_aliases_onlyset to true; with false they fail withE_NOTIMPL(0x80004001). - Administrators can rename items; prefer aliases to names.
- Object type names depend on the user: in the Sample Vault, object type 15 ("Reports") comes back with an empty name for a normal user (see the output above).
GetObjectTypesreturns only real object types (11 in the Sample Vault, for a normal user and for an administrator alike); value lists share their ID space but are not in that list.- The response field of
GetObjectTypeis namedputf8szobjtypebasic.
See also
- Vault and metadata structure: the ID spaces and the built-in IDs.
- Properties and values