# Search for documents

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/search/

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](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCSearch/SearchObjects/index.md) with:

| Field | Value |
|---|---|
| `conditions` | One [SearchConditionArray](https://developer.m-files.com/APIs/gRPC-API/Reference/Messages/MFiles.SearchConditionArray/index.md) with the four conditions below; the conditions inside it are ANDed (SearchObjects takes exactly one array) |
| `sorting` | One [SortRule](https://developer.m-files.com/APIs/gRPC-API/Reference/Messages/MFiles.SortRule/index.md): `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](https://developer.m-files.com/APIs/gRPC-API/Reference/Messages/MFiles.SearchResultsItem/index.md); `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](https://developer.m-files.com/APIs/gRPC-API/Reference/Messages/MFiles.SearchCondition/index.md) or [TypedValue](https://developer.m-files.com/APIs/gRPC-API/Reference/Messages/MFiles.TypedValue/index.md)
as in the tables above.

### Python

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

### C#

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

### Go

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

Output with the defaults (class 1, "Other Document" in the Sample Vault; 30 days) and limit 2:

```text
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_DELETED` equal 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_sort` is true.
* **Enum flag messages.** `SortRuleFlags` and `SearchFlags` are messages with one boolean per
  flag, defined in `enums.proto` (in Python: `enums_pb2.SortRuleFlags`).
* **Typed values must match the property's data type.** A timestamp property takes a `timestamp`
  value; a lookup property takes a `lookup` whose `value_list_item_info.obj_id` names 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](https://developer.m-files.com/APIs/gRPC-API/guides/vault-structure/index.md).
* An object type condition with a plain integer value fails with `0x80040027` "Invalid search
  condition."; use a lookup.

## See also

* [Search](https://developer.m-files.com/APIs/gRPC-API/concepts/search/index.md): the condition model and more condition shapes.
* [Page through results](https://developer.m-files.com/APIs/gRPC-API/guides/paging/index.md) for result sets larger than one call should return.
