Given a query for members of a particular directory role, I would like to return a list of corresponding users. What I have is this:
IDirectoryObjectCollectionWithReferencesRequest request = graphServiceClient.directoryRoles(roleId).members().buildRequest();
IDirectoryObjectCollectionWithReferencesPage page = request.select(USER_FIELDS_TO_RETURN).get();
List<DirectoryObject> objects = page.getCurrentPage();
IDirectoryObjectCollectionWithReferencesRequestBuilder builder = page.getNextPage();
while (builder != null) {
request = builder.buildRequest();
page = request.select(USER_FIELDS_TO_RETURN).get();
objects.addAll(page.getCurrentPage());
builder = page.getNextPage();
}
return objects.stream().filter(o -> o.oDataType.equals("#microsoft.graph.user")).map(o -> new User()).collect(Collectors.toList());
The question lies in the return statement. Filter on only user objects (couldn't find a more elegant way of doing this than comparing the oDataType) and return the user object with the contents of o:
objects.stream().filter(o -> o.oDataType.equals("#microsoft.graph.user")).map(o -> {
// the only thing that I could think of is to do some weird
// serialization/deserialization logic here which is a bad solution
// for anything other than a small number of elements
}).collect(Collectors.toList());
what is the correct way of converting DirectoryObject to User
Microsoft Graph does not currently support this requirement.
If you're checking a specific directoryRole, you could come at this from the other direction. The /members endpoint does support filtering by member id:
v1.0/directoryRoles/{role-id}/members?$filter=id eq '{user-id}'
Please check the answers and workarounds provided in this thread. How to get admin roles that I am a member of, from Microsoft Graph using .Net Client SDK?
I know this is an old question, but I had the same problem and found a better solution.
You can actually convert it to a user after you have the list. So if you are iterating through the list:
var myDirectoryList = (List<DirectoryObject>)myRetrievedList;
foreach(var item in myDirectoryList)
{
var myUser = (User)item;
Console.WriteLine($"My name is {myUser.GivenName}");
}
Where DirectoryObject is Microsoft.Graph.DirectoryObject and User is Microsoft.Graph.User.
Just had the same problem, so, for anyone getting there, here is what i did (And i could not find any other simple solution...).
What you call "some weird serialization/deserialization logic" can actually be done this way using the DefaultSerializer :
private ISerializer serializer = new DefaultSerializer(new DefaultLogger());
...
objects.stream().filter(o -> o.oDataType.equals("#microsoft.graph.user")).map(o -> {
return serializer.deserializeObject(o.getRawObject().toString(), User.class)
}).collect(Collectors.toList());
Related
I am using Kotlin in a webserver app and I have a line of code as follows:
.onComplete { jsonResult: AsyncResult<JsonObject>? ->
Now what I want to do is change the underlying JsonObject wrapped in the AsyncResult, so that it is going to be reflected further downstream.
var res: JsonObject? = jsonResult?.result()
if (res != null) {
if (res.getInteger("files_uploaded") > 0) {
res.put("URL", "Some URL")
}
}
I was then imagining to update the underlying JSON object in the result but not sure how to do that.
please take note that single quotes are missing and ` appear as \` because the code formatting. I tried to leave what seemed least confusing...
You should be able to make changes in the conditional statement
if (res !=null) {
res being the JsonObject:
console.log(res);
would show you what's in there. You may need to use
let resXmodifiedX = JSON.parse(res);
One approach is to write a function and pass res to that function which you can do if it is in the console.log(res).
Some notes on what's below:
place the function somewhere consistent maybe at the bottom of the file...
objects often have multiple levels res.person.name, res.contact.email, or whatever...
use multiple for loops:
let level = res[key]; for(child in level) {
you don't need to do this if you know exactly what object attributes you need to update.
you can set the value directly but you always want to test for it before trying to set it to avoid errors that stop execution.
let toBe = toBe =>`${toBe}`;
let update = (res)?toBe(update(res)):toBe('not Found');
This option is really only if you know for sure that data will be there and you can't proceed without it. Which is not uncommon but also not how JSON is designed to be used.
The code below is a concise way to make some simple changes but may not be an ideal solution. To use it xModify(res) replaces console.log(res) above.
function xModify(x) {
let resXmodifiedX = JSON.parse(x);
let res = resXmodifiedX;
for (key in res) {
res[key] = key=='name'? \`My change ${res[key]}\`: key=='other'? \`My Change ${res[key]}\`:res[key];
resXmodifiedX = JSON.stringify(res);
return resXmodifiedX;
}
That will update res.name and res.other otherwise res[key] is unchanged. If you do not need to parse res change let res = xModifiedx; to let res = x; remove the first line and change the last two lines to return res;
function xModify(x) {
let res = x;
for (key in res) {
res[key] = key=='name'? \`My change ${res[key]}\`: key=='other'? \`My Change ${res[key]}\`:res[key];
return res;
}
If your data is numeric which is not generally the case in a web server response scenario this is a terrible approach. Because it is probably a string I used the template variable as a way to easily add a complex pattern in place of a string. My change ${res[key]} not a real world example. Any valid JS code can go in the ${ } (template variable). I've been defaulting to the first pattern more and more.
let me = (bestCase)?`${'the best version'} of myself`:`${'someone'} I'm ok with`;
This is my groovy class
Asset {
ObjectState objectState = ObjectState.CURRENT
String description
#NotEmpty(message = "*Please provide a asset name")
#Length(min = 2, max = 50, message = "*Asset name must have characters between 2 and 50")
String assetName
#DBRef
Company company
}
I want to find those assets of a particular company which contains "test" in assetName and description
Now i implemented the business logic like this
#Override
Page<Asset> fetchAssetsBySearchStringAndObjectStateAndCompany(Company company, Pageable pageable, String searchQuery) {
ObjectState objectState = ObjectState.CURRENT
if (!pageable) {
pageable = PageRequest.of(0, 10, Sort.Direction.DESC, "lastUpdated")
}
if (searchQuery) {
Page<Asset> assets = assetRepository.findAllByCompanyAndObjectState(company, pageable, objectState)
List<Asset> filteredAssets = []
assets.each {
if (it.assetName.contains(searchQuery) || it.description.contains(searchQuery)) {
filteredAssets.add(it)
}
}
return filteredAssets // i want this list in pagination object
} else {
return assetRepository.findAllByCompanyAndObjectState(company, pageable, objectState)
}
}
I find all the assets of a company -
Filter out the "test" string using groovy closure - assets.each { }
Now my filteredAssets contains required result but i want this in pagination object
Now my question is
1- Is this approach is efficient
2- How to convert filteredAssets in Page
I also tried to use mongo native query but i am unable to convert it to spring boot
#Query('''{
'company': ?0,
$or :
[
{'assetName' : { $regex: ?1, $options:'i' }},
{'description' : { $regex: ?1, $options:'i' }},
]
}
''')
Page<Asset> findAllByCompanyAndAssetNameOrDescription(Company company, String assetName, Pageable pageable)
I don't have a specific answer but my suggestion is that your first approach is not going to work at a higher level because you are filtering the results after the pagination has been performed by the initial query. So you will potentially end up with less than the desired page size (or even an empty result) even though there are valid results that could have been returned by the query.
In other words, to achieve this you really do need to use the second approach of constructing a native query that incorporates the filtering. To resolve why that is not working, you would need to post more information about the kind of errors you are seeing (or possibly put it as a separate question and close this one out).
EDIT: to answer the question more specifically - if you choose to persist with the approach, it looks to me like you can construct your own Page object by harnessing the Spring data PageImpl object which has a usable constructor from a list of elements. You can simply construct this object from your filtered list of elements - ie: instead of this:
...
return filteredAssets
Do this:
return new PageImpl(filteredAssets)
If you want to be more idiomatic with your groovy code I would also suggest to change the filtering operation to use findAll. In that case the code gets more compact:
return new PageImpl(assets.findAll { it.assetName.contains(searchQuery) })
Once again though I would caution that from looking at your problem I don't think it's going to have the result you actually want.
UPDATE: Spuggiehawk advised in his answer to fix the include keyword issue, and also suggest an alternative way to get the _id other than projections. However, I still have trouble to call this method from my service class, which edit the user detail, that I must admit I have limited knowledge to make it right.
#Override
public User get(Object userId) {
FindIterable<User> userTbl = database.getCollection("User", User.class).find();
User user = new User();
for (User doc : userTbl) {
String oid = doc.getId().toHexString();
System.out.println("_id = " + oid);
return user;
}
return null;
}
In the Service class
public void editUser() {
String userId = request.getParameter("id");
User user = userDAO.get(userId);
System.out.println(user.getFullName());
}
You don't need to use projection if you just want the object ID. The syntax you want to get that (in a loop) is:
FindIterable<Document> userTbl = db.getCollection("User").find();
for (Document doc: userTbl2)
{
String id = doc.getObjectId("_id").toString();
System.out.println("_id = " + id);
}
Do what you need to with that id value.
As far as your use of include is concerned, if you do find a situation where you need that, then you need the static import. Eclipse should give you the option if you hover over the keyword:
If Eclipse doesn't show that, you might need to add the references in your Eclipse configuration under Window -> Preferences -> Java -> Editor -> Content Assist -> Favorites:
The important part is at the top of your code, it should include:
import static com.mongodb.client.model.Projections.include;
You'll find that useful for your filters too, eg.
Bson filter = eq("email", "email.com");
db.getCollection("User").find(filter);
Finally, if you only want to get the first matching record in your find(), use:
Document = db.getCollection("User").find(filter).first();
I've got a database of playerdata that has some pre-existing fields from previous versions of the program. Example out-dated document:
{
"playername": "foo"
}
but a player document generated under the new version would look like this:
{
"playername": "bar",
"playercurrency": 20
}
the issue is that if I try to query playercurrency on foo I get a NullPointerException because playercurrency doesn't exist for foo. I want to add the playercurrency field to foo without disturbing any other data that could be stored in foo. I've tried some code using $exists Example:
players.updateOne(new Document("playername", "foo"), new Document("$exists", new Document("playername", "")));
players.updateOne(new Document("playername", "foo"), new Document("$exists", new Document("playercurrency", 20)));
My thought is that it updates only playercurrency because it doesn't exist and it would leave playername alone becuase it exists. I might be using exists horribly wrong, and if so please do let me know because this is one of my first MongoDB projects and I would like to learn as much as I possibly can.
Do you have to do this with java? Whenever I add a new field that I want to be required I just use the command line to migrate all existing documents. This will loop through all players that don't have a playercurrency and set it to 0 (change to whatever default you want):
db.players.find({playercurrency:null}).forEach(function(player) {
player.playercurrency = 0; // or whatever default value
db.players.save(player);
});
This will result in you having the following documents:
{
"playername" : "foo",
"playercurrency" : 0
}
{
"playername" : "bar",
"playercurrency" : 20
}
So I know that it is normally frowned upon on answering your own question, but nobody really posted what I ended up doing I would like to take this time to thank #Mark Watson for answering and ultimately guiding me to finding my answer.
Since checking if a certain field is null doesn't work in the MongoDB Java Driver I needed to find a different way to know when something is primed for an update. So after a little bit of research I stumbled upon this question which helped me come up with this code:
private static void updateValue(final String name, final Object defaultValue, final UUID key) {
if (!exists(name, key)) {
FindIterable iterable = players.find(new Document("_id", key));
iterable.forEach(new Block<Document>() {
#Override
public void apply(Document document) {
players.updateOne(new Document("_id", key), new Document("$set", new Document(name, defaultValue)));
}
});
}
}
private static boolean exists(String name, UUID key) {
Document query = new Document(name, new Document("$exists", true)).append("_id", key);
return players.count(query) == 1;
}
Obviously this is a little specialized to what I wanted to do, but with little revisions it can be easliy changed to work with anything you might need. Make sure to replace players with your Collection object.
I'm iterating a set object to find a particular value. Is there any short way to fetch instead of iterating it? Here is my code
for(Tree t : assignedTrees) {
println t.treeName;
}
The above code will return the expected value.
assignedTrees is the set object
Set<Tree> assignedTrees = new HashSet<Tree>()
println assignedTrees will return
[Tree{id=null, treeName=Mango}]
Can I fetch the treeName instead of iterating?
You can fetch an object from a set by calling mySet.get(object). However, in your case you wish to fetch an object based on one of its attributes. The best way to do this is with a map - e.g.
Map<String, Tree> trees = new HashMap<>();
trees.put(treeObject.treeName, treeObject);
Tree myTree = trees.get("myTreeName");
Note that if you're putting your own objects into sets or maps, you must override the equals and hashcode methods, or very strange things will happen.
In general you can use lambda to find any/first element that fullfils any condition. For example:
Set<Integer> coolStrings = new HashSet<String>();
coolStrings.add("HEHE")
coolStrings.add("NOPE")
coolStrings.add("JP2GMD")
coolStrings.add("1234")
try{
String nice =
coolStrings.stream().filter(
(str) -> { return str.equals("JP2GMD") ||
str.equals("2137"); }
}).findFirst().get();
)
System.out.println("Yay, i found a REALLY cool string! : " + nice);
}
catch(NoSuchElementException e){
System.out.println("Not even one awesome string was found :(");
}
It will print "JP2GMD"
(I didn't compile it, there might be some minor syntax errors)
Working with Stream class is extremally handy (as for java standards)