Search for documents
Search is how you find objects in M-Files: there are no folder paths to walk. This recipe finds the documents of one class modified in the last N days, newest first, and stops after a limit.
Calls
SearchObjects with:
| Field | Value |
|---|---|
conditions |
One SearchConditionArray with the four conditions below; the conditions inside it are ANDed (SearchObjects takes exactly one array) |
sorting |
One SortRule: key 21 (Last modified), options.sort_descending true |
limit |
The maximum number of results |
| Condition | expression |
type |
value |
|---|---|---|---|
| Object type is Document | status_value, STATUS_TYPE_OBJECT_TYPE |
CONDITION_TYPE_EQUAL |
lookup: value list -11, item 0 |
| Not deleted | status_value, STATUS_TYPE_IS_DELETED |
CONDITION_TYPE_EQUAL |
boolean false |
| Class | property_value, property_def 100 |
CONDITION_TYPE_EQUAL |
lookup: value list 1, item = class ID |
| Modified since | property_value, property_def 21 |
CONDITION_TYPE_GREATER_THAN_OR_EQUAL |
timestamp |
Each result is a SearchResultsItem; object.version_info has
the title, version and last_modified_at_utc, object.object_info.obj_id the object's ID.
has_more_results is true when the limit cut the results short.
Code
The condition and value builders (is_object_type, is_not_deleted, property_condition,
lookup, timestamp and their C# and Go counterparts) are small functions of the samples'
helpers (mfiles_values.py, MFilesValues.cs, mfilesclient); each builds one
SearchCondition or TypedValue
as in the tables above.
Python
since = datetime.now(timezone.utc) - timedelta(days=days)
request = IRPCSearch_pb2.SearchObjectsRequest(
# One array: all its conditions must match.
conditions=[structs.SearchConditionArray(value=[
is_object_type(0), # documents
is_not_deleted(),
# Property 100 "Class" is a lookup into value list 1, the classes.
property_condition(100, enums.CONDITION_TYPE_EQUAL, lookup(1, class_id)),
# Property 21 "Last modified" is a timestamp.
property_condition(21, enums.CONDITION_TYPE_GREATER_THAN_OR_EQUAL,
timestamp(since)),
])],
# Newest first. The server sorts all matches, then returns the first `limit`.
sorting=structs.SearchResultsSorting(rules=structs.SortRuleValueArray(values=[
structs.SortRule(key=21, options=enums.SortRuleFlags(sort_descending=True))])),
limit=limit)
search = client.stub(IRPCSearch_pb2_grpc.IRPCSearchStub)
response = search.SearchObjects(request)
Source: python/recipes/search.py
C#
var since = DateTime.UtcNow.AddDays(-days);
var request = new SearchObjectsRequest
{
// One array: all its conditions must match.
Conditions =
{
new SearchConditionArray
{
Value =
{
IsObjectType(0), // documents
IsNotDeleted(),
// Property 100 "Class" is a lookup into value list 1, the classes.
PropertyCondition(100, ConditionType.Equal, Lookup(1, classId)),
// Property 21 "Last modified" is a timestamp.
PropertyCondition(21, ConditionType.GreaterThanOrEqual, Timestamp(since)),
},
},
},
// Newest first. The server sorts all matches, then returns the first `limit`.
Sorting = new SearchResultsSorting
{
Rules = new SortRuleValueArray
{
Values = { new SortRule { Key = 21, Options = new SortRuleFlags { SortDescending = true } } },
},
},
Limit = (uint)limit,
};
var search = new IRPCSearch.IRPCSearchClient(connection.CallInvoker);
var response = await search.SearchObjectsAsync(request);
Source: csharp/Recipes/Search.cs
Go
since := time.Now().UTC().AddDate(0, 0, -int(days))
request := &mfiles.SearchObjectsRequest{
// One array: all its conditions must match.
Conditions: mfilesclient.AllOf(
mfilesclient.IsObjectType(0), // documents
mfilesclient.IsNotDeleted(),
// Property 100 "Class" is a lookup into value list 1, the classes.
mfilesclient.PropertyCondition(100, mfiles.ConditionType_CONDITION_TYPE_EQUAL,
mfilesclient.Lookup(1, classID)),
// Property 21 "Last modified" is a timestamp.
mfilesclient.PropertyCondition(21,
mfiles.ConditionType_CONDITION_TYPE_GREATER_THAN_OR_EQUAL,
mfilesclient.Timestamp(since)),
),
// Newest first. The server sorts all matches, then returns the first `limit`.
Sorting: mfilesclient.SortBy(21, true),
Limit: uint32(limit),
}
search := mfiles.NewIRPCSearchClient(client.Conn)
response, err := search.SearchObjects(ctx, request)
if err != nil {
return err
}
Source: go/recipes/search/main.go
Output with the defaults (class 1, "Other Document" in the Sample Vault; 30 days) and limit 2:
Documents of class 1 modified since 2026-10-01: 2 (limited to 2; more exist)
[docs-test] Created by the Python sample 2026-10-31 15:06:31 (ID 629, version 1, modified 2026-10-31 15:06:31 UTC)
[docs-test] Created by the Python sample 2026-10-31 15:06:29 (ID 628, version 1, modified 2026-10-31 15:06:29 UTC)
The recipe's arguments are the class ID (default 1), the number of days (default 30) and the limit (default 10).
Pitfalls
- Deleted objects. A user who can see deleted objects, such as an administrator, gets them in
the results unless a condition excludes them. Add
STATUS_TYPE_IS_DELETEDequal to false to every search that should find only live objects. - One sort rule. The server sorts by one rule at most. The property must be searchable and
not multi-line text; otherwise the search fails, or falls back to the default order when
sorting.options.allow_resorting_to_default_sortis true. - Enum flag messages.
SortRuleFlagsandSearchFlagsare messages with one boolean per flag, defined inenums.proto(in Python:enums_pb2.SortRuleFlags). - Typed values must match the property's data type. A timestamp property takes a
timestampvalue; a lookup property takes alookupwhosevalue_list_item_info.obj_idnames the value list (type) and the item (item_id.internal_id). - IDs are vault-specific except the built-in ones (property 21 Last modified, 100 Class; object type 0 Document). Resolve the others by alias: see Read the vault structure.
- An object type condition with a plain integer value fails with
0x80040027"Invalid search condition."; use a lookup.
See also
- Search: the condition model and more condition shapes.
- Page through results for result sets larger than one call should return.