I'm using Microsoft Graph client and want to retrieve users based on a list of objectIds. So far I've managed to do it like this:
// Create filterstring based on objectId string list.
var filterString = string.Join(" or ", objectIds.Where(x => !string.IsNullOrEmpty(x)).Select(objectId => $"id eq '{objectId}'"));
// Get users by filter
var users = await _graphServiceClient.Users.Request()
.Select(x => new { x.UserPrincipalName, x.Id })
.Filter(filterString)
.GetAsync(ct).ConfigureAwait(false);
But I've hit this error here:
Too many child clauses specified in search filter expression containing 'OR' operators: 22. Max allowed: 15.
Is there another way to only get a portion of users? Or do I need to "chunk" the list up in 15 each?
You should probably split your query and send a BATCH request to the Graph API.
This will send only 1 request to the server, but allow you to query for more data at once.
https://learn.microsoft.com/en-us/graph/sdks/batch-requests?tabs=csharp
This could look something like this: (untested code)
var objectIds = new string[0];
var batchRequestContent = new BatchRequestContent();
var requestList = new List<string>();
for (var i = 0; i < objectIds.Count(); i += 15)
{
var batchObjectIds = objectIds.Where(x => !string.IsNullOrEmpty(x)).Skip(i).Take(15);
var filterString = string.Join(" or ", batchObjectIds.Select(objectId => $"id eq '{objectId}'"));
var request = _graphServiceClient.Users.Request()
.Select(x => new { x.UserPrincipalName, x.Id })
.Filter(filterString);
var requestId = batchRequestContent.AddBatchRequestStep(request);
requestList.Add(requestId);
}
var batchResponse = await _graphServiceClient.Batch.Request().PostAsync(batchRequestContent);
var allUsers = new List<User>();
foreach (var it in requestList)
{
var users = await batchResponse.GetResponseByIdAsync<GraphServiceUsersCollectionResponse>(it);
allUsers.AddRange(users.Value.CurrentPage);
}
Related
I am selecting data from a data store
I am able to fetch first array [0] {IHSWCFService.ServiceReference1.Observation} using below query
var newData = data.Select(a => new IHSData
{
PriceSymbol = Convert.ToString(a.PriceId),
PeriodData = Convert.ToDateTime(a.ObservationVector.Select(x => x.Period).FirstOrDefault()),
StatusID = Convert.ToInt32(a.ObservationVector.Select(x => x.StatusId).ToList()),
Price = Convert.ToDouble(a.ObservationVector.Select(x => x.price).FirstOrDefault()),
});
But I want to select next array also. as showing in below screen screenshot
[0]{IHSWCFService.ServiceReference1.Observation}
[1]{IHSWCFService.ServiceReference1.Observation}
[2]{IHSWCFService.ServiceReference1.Observation}
Could you please help me. Thanks
You might want all your properties in IHSData to be lists:
var newData = data.Select(a => new IHSData
{
PriceSymbol = Convert.ToString(a.PriceId),
PeriodData = a.ObservationVector.Select(x => Convert.ToDateTime(x.Period)).ToList(),
StatusID = a.ObservationVector.Select(x => Convert.ToInt32(x.StatusId)).ToList(),
Price = a.ObservationVector.Select(x => Convert.ToDouble(x.price)).ToList(),
});
Which is not such a good idea, because you have to index them separately. So another option would be to use SelectMany:
var newData = data
.SelectMany(a => a.ObservationVector.Select(v =>
new IHSData
{
PriceSymbol = Convert.ToString(a.PriceId), // parent PriceId
PeriodData = Convert.ToDateTime(v.Period),
StatusID = Convert.ToInt32(v.StatusId),
Price = Convert.ToDouble(v.price),
}))
.ToList();
The latter approach will create a separate IHSData instance for each ObservationVector, and some of them will share the same PriceId of the parent class.
Or, the third approach would be to have a new class, which would be the "parsed version of the ObservationVector", i.e. contain properties for parsed values, something like:
var newData = data.Select(a => new IHSData
{
PriceSymbol = Convert.ToString(a.PriceId),
Data = a.ObservationVector.Select(x => ConvertObservationVector(x)).ToList()
});
where ConvertObservationVector is a method which converts from an ObservationVector to your parsed class.
I've been trying to fetch document from elastic search on the basis of two fields "sAMAccountName" and "Container" but no luck.
enter image description here
What i want is a document where both attributes are exactly equally to my given value.
This is something I've been doing
It gave me results where user contain's the given property but i want exact match.
string container = getUserContainer(identityStore.ConnectionString);
var searchRequest = new Nest.SearchRequest();
searchRequest.Size = 10000;
searchRequest.Query = GetQuery(knownAttributes.SamAccountName, userName) && GetQuery("Container", container);
var searchResults = elasticSearch.Search<Dictionary<string,object>>(searchRequest);
return null;
private Nest.MatchQuery GetQuery(Nest.Field field, string query)
{
var matchQuery = new Nest.MatchQuery();
matchQuery.Field = field;
matchQuery.Query = query;
matchQuery.Operator = Operator.And;
r
eturn matchQuery;
}
Try this:
IQueryResponse<T> result = client.Search<T>(s => s
.Size(1)
.Query(q => q.Term("field1", value1) && q.Term("field2", value2))
.Index("myindex")
.Type("mytype")
);
if (result.Documents.Count<T>() > 0)
{
//do something
}
I've got a database containing several collections. Some has got a name like "carpenter_title_year_version". Others has got a name like "plumber_title_year_version". How do I set up a filter to retrieve all collections where the string "carpenter" is in the collectionname?
I'm thinking something like:
var filterBuilder = Builders<GroupEntity>.Filter;
var projectionBuilder = Builders<GroupEntity>.Projection;
var collection = Database.GetCollection<GroupEntity>("dbname");
var filter = filterBuilder.ElemMatch("carpenter..., ?"); //<--- ???
var projection = projectionBuilder.Exclude("_id");
var list = await collection.Find(filter).Project(projection).ToListAsync();
There's (and an async version of it)
IAsyncCursor<MongoDB.Bson.BsonDocument> IMongoDatabase.ListCollections(ListCollectionsOptions options = null, CancellationToken cancellationToken = null);
You can use the filter from the collection options to match the collection name.
// <CAPS INSIDE> are wildcards
var _client = new MongoClient(#"connection string");
var _database = _client.GetDatabase("<DATABASE NAME>");
var bre = new BsonRegularExpression("<YOUR REGEX PATTERN>");
var copt = new ListCollectionsOptions{ Filter =
Builders<BsonDocument>.Filter.Regex ("name", bre )
};
var collectionsWithMatchingNames = _database.ListCollections(copt).ToList().Select (col => col["name"]);
Only then you get your particular collection, like:
foreach (var x in collectionsWithMatchingNames)
var collection = _database.GetCollection<BsonDocument>(x);
I need to modify and existing Azure Table Storage query, assuming i is an integer query retrieves latest report:
string rowCompare = String.Format(CommonDefs.inverseTimeStampRowKeyFormat, DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks);
var result = (from er in this.serviceContext.EntityReportsTable
where er.PartitionKey.Equals(i.ToString(), StringComparison.OrdinalIgnoreCase) && er.RowKey.CompareTo(rowCompare) > 0
select er).Take(1)).FirstOrDefault();
I need to modify it to retrieve latest reports for several known entities, replacing single integer i with array of integers - like int[]{1, 6, 10}.
Apart from running existing query sequentially for the each parameter in array, is there a way to do it in one query? Like IN clause in Sql?
You can use the lastest version of the Azure Storage Client Library this is the complete pseudo code for your task:
var rowCompare = String.Format("{0}", DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks);
var items = new []{"1", "6", "10"};
var filters =
items.Select(key => TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, key)).ToArray();
var combine =
filters.Length > 0
? filters[0]
: null;
for (var k = 0; k < filters.Length; k++)
combine = TableQuery.CombineFilters(combine, TableOperators.Or, filters[k]);
var final = TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThan, rowCompare);
if (!string.IsNullOrEmpty(combine))
final = TableQuery.CombineFilters(final, TableOperators.And, combine);
var query = new TableQuery<EntityReport>().Where(final);
var client = CloudStorageAccount.DevelopmentStorageAccount.CreateCloudTableClient();
var table = client.GetTableReference("EntityReports");
var result = table.ExecuteQuery(query);
Azure Table Storage does not support IN clause like SQL. However instead of doing a query sequentially, you could fire queries in parallel and compare the result. For example look at the pseudo code below:
List<Task<T>> tasks = new List<Task<T>>();
foreach (var i in integerArray)
{
tasks.Add(Task.Factory.StartNew<T>(() => {
string rowCompare = String.Format(CommonDefs.inverseTimeStampRowKeyFormat, DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks);
var result = (from er in this.serviceContext.EntityReportsTable
where er.PartitionKey.Equals(i.ToString(), StringComparison.OrdinalIgnoreCase) && er.RowKey.CompareTo(rowCompare) > 0
select er).Take(1)).FirstOrDefault();
}));
}
Task.WaitAll(tasks.ToArray());
foreach (var task in tasks)
{
var queryResult = task.Result;
//Work on the query result
}
Currently i'd like to find all groups within the Active Directory where the current user has the right WriteProperty.
The problem is that i can find all groups where the user directly is inserted, but when the user is within a group and that group has write access it won't show up. I thought that setting the booleans of GetAccessRules() would help here, but it doesn't.
So here is the code i already have:
var identity = WindowsIdentity.GetCurrent().User;
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();
var allSearcher = allDomains.Select(domain =>
{
var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));
//Apply some filter to focus on only some specfic objects
searcher.Filter = "(&(objectClass=group)(name=*part_of_group_name*))";
return searcher;
});
var itemsFound = allSearcher
.SelectMany(searcher => searcher.FindAll()
.Cast<SearchResult>()
.Select(result => result.GetDirectoryEntry()));
var itemsWithWriteAccess = itemsFound
.Where(entry => entry.ObjectSecurity.GetAccessRules(true, true, typeof(SecurityIdentifier))
.Cast<ActiveDirectoryAccessRule>()
.Where(rule => rule.IdentityReference == identity)
.Where(rule => (rule.ActiveDirectoryRights & ActiveDirectoryRights.WriteProperty) == ActiveDirectoryRights.WriteProperty)
.Count() > 0);
foreach (var item in itemsWithWriteAccess)
{
Debug.Print(item.Name);
}
After a long time and the help of Harvey through this question i finally found a good working solution.
As already explained by Harvey it can be a little difficult to really further understand what you'll get back in entry.Properties["allowedAttributesEffective"].Value. But for normal purposes all you have to check for a write permission is that this field is simply not null.
Here is the sample code:
// (replace "part_of_group_name" with some partial group name existing in your AD)
var groupNameContains = "part_of_group_name";
var identity = WindowsIdentity.GetCurrent().User;
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();
var allSearcher = allDomains.Select(domain =>
{
var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));
// Apply some filter to focus on only some specfic objects
searcher.Filter = String.Format("(&(objectClass=group)(name=*{0}*))", groupNameContains);
return searcher;
});
var directoryEntriesFound = allSearcher
.SelectMany(searcher => searcher.FindAll()
.Cast<SearchResult>()
.Select(result => result.GetDirectoryEntry()));
var allowedTo = directoryEntriesFound.Select(entry =>
{
using (entry)
{
entry.RefreshCache(new string[] { "allowedAttributesEffective" });
var rights = entry.Properties["allowedAttributesEffective"].Value == null ? "read only" : "write";
return new { Name = entry.Name, AllowedTo = rights };
}
});
foreach (var item in allowedTo)
{
var message = String.Format("Name = {0}, AllowedTo = {1}", item.Name, item.AllowedTo);
Debug.Print(message);
}