I'm trying to figure out how to query hierarchical tree structures in Hazelcast. Let's say I have an Organization class:
public class Organization {
private long id;
private long parentId;
}
and I have a User class:
public class NestupUser extends BaseEntity {
private long id;
private String firstName;
private String lastName;
private String email;
private String password;
private long organizationId;
}
Now, given an organizationId I want to find all users for that organization and all organizations which have that organization as a parent, have those organizations as a parent, etc.
I assume this would work as some sort of MapReduce, but is it possible to kick off more MapReduce tasks as part of one MapReduce?
Any help appreciated.
I ended up building a denormalized multimap so I could find all of the reachable organizations for a given org id. This is the code that sets up the structure on startup if it's not already set up by another node. This class also implements the entry listener interfaces to get callbacks when things change to keep the structure in sync (not shown, but not hard to do):
#PostConstruct
public void init() {
IMap<String, Organization> organizationMap = organizationService.getMap();
listenerRegistration = organizationMap.addLocalEntryListener(this);
MultiMap<String, String> orgStructureMap = getOrgStructureMap();
if (orgStructureMap.keySet().size() == 0) {
Collection<Organization> all = organizationService.getAll(null);
Set<String> visited = new HashSet<>();
for (Organization next : all) {
if (!visited.contains(next.getId())) {
while (next != null && next.getParentId() != null && !visited.contains(next.getParentId())) {
next = next.getParentOrganization();
}
recurseReferences(visited, next);
}
}
}
}
private void recurseReferences(Set<String> visited, Organization org) {
addAllReferences(org);
visited.add(org.getId());
Set<Organization> childOrganizations = org.getChildOrganizations();
for (Organization child : childOrganizations) {
recurseReferences(visited, child);
}
}
private void addAllReferences(Organization organization) {
MultiMap<String, String> orgStructureMap = getOrgStructureMap();
String parentId = organization.getParentId();
if (parentId != null) {
Set<Map.Entry<String, String>> entries = orgStructureMap.entrySet();
for (Map.Entry<String, String> next : entries) {
if (next.getValue().equals(parentId)) {
orgStructureMap.put(next.getKey(),organization.getId());
}
}
}
orgStructureMap.put(organization.getId(), organization.getId());
}
private void removeAllReferences(Organization organization) {
MultiMap<String, String> orgStructureMap = getOrgStructureMap();
Set<String> keys = orgStructureMap.keySet();
for (String key : keys) {
orgStructureMap.remove(key, organization.getId());
}
}
Related
I have the following class:
#DynamoDBTable(tableName = "PodcastReviewService-Episodes")
public class Episode {
private String podcast;
private int episodeNr;
private String name;
private int avgRating;
private String episodeId;
private List<Review> reviews;
public Episode() {}
#DynamoDBIndexHashKey(attributeName = "podcast")
public String getPodcast() {return podcast;}
#DynamoDBRangeKey(attributeName = "episodeNr")
public int getEpisodeNr() {return episodeNr;}
#DynamoDBAttribute(attributeName = "name")
public String getName() {
return name;
}
#DynamoDBIndexHashKey(attributeName = "avgRating", globalSecondaryIndexName = "avgRating")
public int getAvgRating() {
return avgRating;
}
#DynamoDBIndexRangeKey(attributeName = "episodeId", globalSecondaryIndexName = "episodeId")
public String getEpisodeId() {
return episodeId;
}
#DynamoDBAttribute(attributeName = "reviews")
public List<Review> getReviews() {
return reviews;
}
The class that attempts to add an object Episode to my DynamoDB table is :
public class EpisodeDao {
private final DynamoDBMapper dynamoDBMapper;
public EpisodeDao() {
this.dynamoDBMapper = new DynamoDBMapper(AmazonDynamoDBClientBuilder.standard().withCredentials(DefaultAWSCredentialsProviderChain.getInstance()).withRegion(Regions.US_WEST_2).build());
}
public Episode getEpisode(String podcast, int episodeNr) {
Episode episode = this.dynamoDBMapper.load(Episode.class, podcast, episodeNr);
if (episode == null) {
throw new EpisodeNotFoundException("Could not find episode of " + podcast + " nr." + episodeNr);
}
return episode;
}
public Episode saveEpisode(String podcast, String name, int episodeNr) {
Episode episode = new Episode();
//Check if episode already exists, if so return an DuplicateEpisodeException
//We initialize average rating at 0 since no reviews have been submitted
if (getEpisode(podcast, episodeNr) == null) {
episode.setPodcast(podcast);
episode.setName(name);
episode.setEpisodeNr(episodeNr);
episode.setEpisodeId(podcast.charAt(0) + name.charAt(0) + PodcastReviewsUtils.generateRandomID());
episode.setAvgRating(0);
episode.setReviews(new ArrayList<>());
} else throw new DuplicateEpisodeException("The episode that you are trying to add seems to already exist.");
return episode;
}
This is the output I get on Lambda:
"errorMessage": "#DynamoDBIndexHashKey must specify one of HASH GSI name/names",
"errorType": "com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException",
"stackTrace": [
"com.amazonaws.services.dynamodbv2.datamodeling.StandardAnnotationMaps$FieldMap.globalSecondaryIndexNames(StandardAnnotationMaps.java:345)",
"com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel$Properties$Immutable.<init>(DynamoDBMapperFieldModel.java:459)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties$Bean.<init>(StandardBeanProperties.java:92)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties$Bean.<init>(StandardBeanProperties.java:86)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties$BeanMap.putOrFlatten(StandardBeanProperties.java:217)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties$BeanMap.putAll(StandardBeanProperties.java:207)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties$BeanMap.<init>(StandardBeanProperties.java:198)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties$CachedBeans.getBeans(StandardBeanProperties.java:55)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties$CachedBeans.access$100(StandardBeanProperties.java:48)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties.of(StandardBeanProperties.java:42)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardModelFactories$TableBuilder.<init>(StandardModelFactories.java:132)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardModelFactories$TableBuilder.<init>(StandardModelFactories.java:116)",
"com.amazonaws.services.dynamodbv2.datamodeling.StandardModelFactories$StandardTableFactory.getTable(StandardModelFactories.java:107)",
"com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.getTableModel(DynamoDBMapper.java:409)",
"com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.load(DynamoDBMapper.java:447)",
"com.amazonaws.services.dynamodbv2.datamodeling.AbstractDynamoDBMapper.load(AbstractDynamoDBMapper.java:85)",
"com.podcast_reviews_service.dynamodb.EpisodeDao.getEpisode(EpisodeDao.java:35)",
"com.podcast_reviews_service.dynamodb.EpisodeDao.saveEpisode(EpisodeDao.java:50)",
"com.podcast_reviews_service.activity.AddEpisodeActivity.handleRequest(AddEpisodeActivity.java:35)",
"java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)",
"java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)",
"java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)",
"java.base/java.lang.reflect.Method.invoke(Unknown Source)"
]
I haven't yet implemented the class that utilizes the GSI. I tried without the secondary index annotation but since my table has them I get the same error.
The error makes me think that I either annotated the Episode class wrong or I'm sending a null value as HASH key for the GSI, which I'm pretty sure I'm not.
You must define the name of your index for the indexhashkey.
#DynamoDBIndexHashKey(attributeName = "podcast",globalSecondaryIndexNames={ "my-index-name"})
public String getPodcast() {return podcast;}
I would like to iterate through a list, get the object's name and match that name with the record in my database. Then fetch that record.
I tried to use the foreach tag in Mybatis but I only get an empty list in return. Please help me out and point out if I have made any mistakes.
For reference, I have attached my Service implementation, DAO, mapper & the list class I'm using.
Service implementation
String jsonStr = "[{\"name\":\"first \",\"value\":\"1 \"},{\"name\":\"second\",\"value\":\"2\"},{\"name\":\"third\",\"value\":\"3\"},{\"name\":\"fourth\",\"value\":\"4\"}]";
List<PaymentCommon.CampaignsRsp> list = null;
try { // creating a list from the json String
list = mapper.readValue(jsonStr, new TypeReference<List<PaymentCommon.CampaignsRsp>>(){});
} catch (IOException e) {
e.printStackTrace();
}
if(JudgeUtils.isNotEmpty(rsp.getCampaigns())) {
listRsp = cmmClmWhitelistingDao.getCampaigns(list); // calling the method
}
else System.out.println("LIST IS EMPTY");
DAO
package com.hisun.lemon.cmm.dao;
import com.hisun.lemon.cio.dto.common.PaymentCommon;
import com.hisun.lemon.cmm.entity.CmmClmCampaignDO;
import com.hisun.lemon.framework.dao.BaseDao;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
#Mapper
public interface ICmmClmWhitelistingDao extends BaseDao {
List<CmmClmCampaignDO> getCampaigns(List<PaymentCommon.CampaignsRsp> campaignList);
}
Mapper
<select id="getCampaigns" resultMap="BaseResultMap" >
SELECT campaign.campaign_id, campaign.campaign_name,
FROM clm_campaign campaign
WHERE campaign.campaign_name IN
<foreach item="id" collection="list" open="[{" separator="},{" close="}]" >
#{id.name}
</foreach>
</select>
PaymentCommon.class
#Json
public static class CampaignsRsp {
#Item
private String name;
#Item
private String value;
public CampaignsRsp() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
I found a solution.
I am now getting the names from the List<PaymentCommon.CampaignsRSp> and making a simple List of those names. Then, I'm passing that list to the mapper, making it simple to iterate the list.
Plus, I was missing the #Param annotation in my DAO, so I added that.
This approach gets me the data I want from my DB.
I've a parent object (Product) and a child (Inventory). I'm trying to retrieve the value of the child object from a DisplayProducts class that I've created.
public class DisplayProducts {
private Product__c products;
public DisplayProducts(Product__c item) {
this.products = item;
}
// Properties for use in the Visualforce view
public String name {
get { return products.Name; }
}
public String colour {
//error here
get { return products.Inventorys__r.Colour__c; }
}
public class Product {
public List<DisplayProducts> getProducts() {
if(products == null) {
products = new List<DisplayProducts>();
for(Product__c item : [Select ProductID__c,Name, Price__c, (SELECT Inventory__c.Size__c, Inventory__c.Colour__c, Inventory__c.Quantity__c FROM Product__c.Inventorys__r)
From Product__c WHERE ProductID__c = :prodID]) {
products.add(new DisplayProducts(item));
}
}
return products;
}
}
I keep getting a compile error: invalid FK relationship.
I tried
products.Inventorys__r[0].Colour__c;
but it will only retrieved the first element.
How do I retrieve the child item via the DisplayProducts Class? Any help will be greatly appreciated.
Thank you.
I have a list of type Myclass
List<Myclass> liSubjectIdDetail = new List<Myclass>();
where Myclass looks like
public class Myclass
{
public Nullable<decimal> SubjectId { get; set; }
public string SubjectName { get; set; }
}
I am adding records into liSubjectIdDetail from a table
foreach (decimal Id in VarCEMSIdDetail)
{
liSubjectIdDetail.AddRange(db.Stt.MyTable.Where(x => x.Id == Id).Select(x => new Myclass { SubjectId = x.SubjectId, SubjectName = x.SubjectName }).ToList());
}
where Id contains a list of certain Ids on the basis of which records I fetch.
Now I want to get only distinct records in this list.
I have tried it with hashtable in place of List
and I also tried
liSubjectIdDetail= liSubjectIdDetail.Distinct().ToList();
but this too, is not working. Please give me a better solution.
Thanks in advance
Try this extension method
public static class IEnumerableExtensions {
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
var seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
}
Usage:
liSubjectIdDetail= liSubjectIdDetail.DistinctBy(s => s.SubjectName).ToList();
I have found a work around for my issue, but I was wondering if someone can explain why this is happening.
I have a class DataCollector.
public class DataCollector
{
public string Name { get; set; }
public string Group { get; set; }
public int Count { get; set; }
public Brush Color { get; private set; }
public DataCollector(string name, string group, int count, Brush color)
{
Name = name;
Group = group;
Count = count;
Color = color;
}
}
In my code I have an ObservableCollection of DataCollector.
public ObservableCollection<DataCollector> dataGrid { get; set; }
I collect my data and use it just fine until when I went to add more features to the code.
I want to go through "dataGrid" and make a list of Groups and get a total count from all Names.
List<DataCollector> dataCollector = new List<DataCollector>();
List<string> _groupNames = new List<string>();
foreach (DataCollector dc in dataGrid)
{
int cnt = 0, index = 0;
bool groupFound = false;
if (dataCollector.Count == 0)
{
_groupNames.Add(dc.Group);
dataCollector.Add(dc));
}
else
{
foreach (string groupNames in _groupNames)
{
if (groupNames == dc.Group)
{
index = cnt;
nameFound = true;
}
cnt++;
}
if (nameFound)
{
// When I do this my dataGrid.Count increments, too
dataCollector[index].Count += dc.Count;
}
else
{
_groupNames.Add(dc.Group);
dataCollector.Add(dc);
}
}
}
To get around this I changed
dataCollector.Add(dc);
to
dataCollector.Add(new DataCollector(dc.Name, dc.Group, dc.Count, dc.Color));
Why did I have to this? Does adding "dc" to "dataCollector" create a link to "dataGrid"? If it does it doesn't make sense.
You do that because dataCollector.Add(dc) copies the reference to the memory location of an object. So changes to the data at the location is reflected in either collection as they hold pointers to the same objects.
What you could do instead is to mimic a copy constructor.
public DataCollector(DataCollector dataCollector)
{
Name = dataCollector.Name;
Group = dc.Group;
//...
}
It sorts of keep your code clean.