# Read the vault structure

Location: [gRPC API](https://developer.m-files.com/APIs/gRPC-API/index.md) > [Guides](https://developer.m-files.com/APIs/gRPC-API/index.md#guides)

HTML version: https://developer.m-files.com/APIs/gRPC-API/guides/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](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectTypes/GetObjectTypes/index.md) | `object_types`: the real object types, not the value lists |
| One object type | [GetObjectType](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectTypes/GetObjectType/index.md) | `putf8szobjtypebasic` |
| Property definitions | [GetPropertyDefs](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCPropertyDefs/GetPropertyDefs/index.md) | `property_defs` |
| Classes and class groups | [GetObjectClassesAndGroups](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCPropertyDefs/GetObjectClassesAndGroups/index.md) | `classes`, `class_groups` |
| ID by alias | [GetMetadataStructureItemIdByAlias](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCVault/GetMetadataStructureItemIdByAlias/index.md) | `id`; -1 (-1000 for named ACLs) if no item has the alias |

* A class ([ObjectClass](https://developer.m-files.com/APIs/gRPC-API/Reference/Messages/MFiles.ObjectClass/index.md)) has its ID and name in
  `base_info.item_info` (`obj_id.item_id.internal_id`, `name`), its object type in `object_type`,
  its properties in `associated_property_defs` (with `is_required`) and its default workflow in
  `default_workflow` (0 for none).
* A property definition ([PropertyDef](https://developer.m-files.com/APIs/gRPC-API/Reference/Messages/MFiles.PropertyDef/index.md)) has `data_type`, for
  lookups the value list it refers to in `value_list`, and `is_predefined` true 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](https://developer.m-files.com/APIs/gRPC-API/concepts/metadata-structure-version-and-caching/index.md).

## 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

```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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/python/recipes/vault_structure.py)*

```python
# 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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/python/recipes/vault_structure.py)*

### C#

```csharp
// 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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/csharp/Recipes/VaultStructure.cs)*

```csharp
// 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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/csharp/Recipes/VaultStructure.cs)*

### Go

```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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/go/recipes/vault_structure/main.go)*

```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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/go/recipes/vault_structure/main.go)*

Output for a normal user of the M-Files Sample Vault, which defines no aliases:

```text
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.** `GetMetadataStructureItemIdByAlias` returns -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_type` selects the ID space: an alias of a property definition is looked up with
  `METADATA_STRUCTURE_ITEM_PROPERTY_DEF`, of a class with `METADATA_STRUCTURE_ITEM_OBJECT_CLASS`.
* To list all aliases, [GetMetadataStructureItemIdsByAliases](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCVault/GetMetadataStructureItemIdsByAliases/index.md)
  and [GetAllMetadataStructureItemIdswithAliases](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCVault/GetAllMetadataStructureItemIdswithAliases/index.md)
  need `unambiguous_aliases_only` set to true; with false they fail with `E_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).
* `GetObjectTypes` returns 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 `GetObjectType` is named `putf8szobjtypebasic`.

## See also

* [Vault and metadata structure](https://developer.m-files.com/APIs/gRPC-API/concepts/vault-and-metadata-structure/index.md): the ID spaces and
  the built-in IDs.
* [Properties and values](https://developer.m-files.com/APIs/gRPC-API/concepts/properties-and-values/index.md)
