I want to retrieve particular list of element from the Mongodb Table.
Lets suppose i have two variables in my Employee class-:
public Class Employee
{
private String Id;
private String Name;
.
.
now when i will make the fetch query it will be something like -:
List<Employee> list=mongoTemplate.findAll();
and then i will loop through each employee object to fetch the employee ID and save in the List<String>
Now, I want the solution in such a way that i can retrieve all the ID's in one go.Something like-:
List<String> employeeId = someCodeHere;
Please help if you can
Thanks in advance.
According to Mongos Reference documentation on distinct operation:
Finds the distinct values for a specified field across a single collection or view and returns the results in an array.
In Spring Data MongoDB this can be achieved like this:
DistinctIterable<String> distinctIds =
mongoTemplate.getCollection(mongoTemplate.getCollectionName(Employee.class))
.distinct("id", String.class);
return Lists.newArrayList(distinctIds);
// or
BasicDBObject dbObject = new BasicDBObject();
dbObject.append("name", new BasicDBObject("$regex", ".*and.*"));
DistinctIterable<String> distinctIds =
mongoTemplate.getCollection(mongoTemplate.getCollectionName(Employee.class))
.distinct("id", dbObject, String.class);
return Lists.newArrayList(distinctIds);
MongoTemplate offers here a couple of overloads for distinct. The primer query will collect all IDs for entries of a employee collection directly while the latter one will perform a filtering for only IDs of employees that contain an and within their name.
In order to convert the iterable result set to the requested List of String objects you can make use of Guava's newArray(...) feature.
As #Veeram also mentioned in his comment you can of course also make use of a projected query like
Query query = Query.query(Criteria.where(...));
query.fields().include("id");
return mongoTemplate.find(query, String.class);
where query.fields().include("id") is used to specify the fields you are actually interested in.
In contrast to distinct, this approach will contain duplicate entries in the result list, if there are any. While an ID should in general be unique, performing these two queries on names might produce a result that contains multiple identical entries however.
While the answer given by #Boris is technically valid as well, it might have some performance impact, unfortunately, especially if lots of embedded and referenced documents need to get retrieved as well. I therefore would not recommend such an approach.
Final note: throughout the examples I have kept the Id and Name fields in lower-case letters as this is basically Java naming convention.
After a year, what you want can be done with the following code:
List<String> employeeIds= mongoTemplate.query(Employee.class).distinct("id").as(String.class).all();
Without the need of making any conversion. I had the same situation and resolved it doing that.
You can use Java Stream API:
private List<String> getEmployeeIds() {
return mongoTemplate.findAll().stream()
.map(Employee::getId)
.filter(Objects::nonNull)
.collect(toList());
}
First you query for all employees, then convert to a stream, map Employee to Id and then aggregate all non-null values to a list.
If your Repository uses a Java Stream query method:
Stream<Employee> findAll();
then you don't need to call stream() method inside getEmployeeIds().
EDIT: added filtering a null value from the Stream
Related
I was asked this question in an interview today to which I explained the best to my abilities. But I still don't understand if this is the correct answer.
There is a cache which has Employee object as the key. The cache is populated with data from the database. Now there is a UI where we can enter either or all of the 3 attributes from the Employee object- name, ID and date of joining. Now this search would lead to multiple matching results. To achieve this we need to check in the cache for the data.
To this I replied saying that my map would be of the structure - >. for the same EmployeeDetails object ,
I will have multiple keys in the map(EmployeeDetails class is the object which contains complete detail of the Employee including address etc. Employee object just has 3 attributes - name, ID and date of joining.).
One of the objects with only name populated. The other with ID populated and the third one with date of joining populated. And now with the combination of attributes. So the map will be having the following keys -
Employee object with only the name populated -> Value would be list of of all the Employee objects with the same name.
Employee object with only the ID populated -> Value would be list of of all the Employee objects with the same ID. Ideally the list size in this case should be 1.
Employee objects with only the Date Of Joining -> List of all the employee objects with the same date of joining.
Similarly there would be number of other Employee objects. For one such employee , all the three attributes - name , ID and date of joining would be populated.
In this way, I could have achieved the requirement to display all the employee results in case only some of the attributes out of name, ID and value is set on the UI.
I just want to understand if this is the correct way to achieve the outcome (display of list of matching results on the UI). Since I did not get selected, I believe there is something else which I possibly missed!
A reasonable short answer is to maintain 3 separate maps for each of the 3 fields, with each one mapping from each field value to the list of employees with that value for the field.
To perform a lookup, retrieve the lists for each of the values that the user specified, and then (if you have more than one criteria) iterate through the shortest one to filter out employees that don't match the other criteria.
In the cases where you have more than one criteria, one of them has to be name or ID. In real life, the lists for these fields will be very short, so you won't have to iterate through any large collections.
This solution essentially uses the maps as indexes and implements the query like a relational DB. If you were to mention that in an interview, you would get extra points, but you'd need to be able to back it up.
One of the neat things about Java 8 is the Streams API. With this new API, you an hold all of those Employee objects within just a normal List and walk away with the same results you were trying to achieve with multiple mapping objects with less overhead.
See, this API has a .filter() method that you can pass over a List that has been transformed into a Stream to only return objects that meet the criteria described in the body of the filter.
List<Employee> emps = getEmps();
List<Employee> matchedEmps = emps.stream().filter((e)->e.getID().equals(searchedID)).filter((e)->e.getName().equals(searchedName)).collect(Collectors.toList());
As you can see you can chain filters to match multiple criteria, although it may be more efficient just to have all matching done in one filter:
ist<Employee> matchedEmps = emps.stream().filter((e)->{boolean matches = e.getID().equals(searchedID);return matches && e.getName().equals(searchedName);}).collect(Collectors.toList());
I would have a map with the Employee object as key and EmployeeDetails as value. I would get a get Collection of values from the map, create then a custom Comparator for each specific search, iterate through the values collection and use the comparator to compare the values. The search results should be added during the iteration in a results Collection.
One way is create mapping with mapping Employee-EmployeeDetails then for search for a given employee id then you have to iterate over all key and search.The complexity will be O(N).
Second to improve the time complexity even in database we do indexing to avoid full scan.You can try the similar thing here i.e create mapping id-Employee,email-Employee like this when add employee to main map also update to the index map.
Third if possible you can create a TRIE and at end node you can put employee.After getting the employee You can get employee details
I have some data that contains a STATE field (String/Text) that defines what state a given request is currently in (e.g. pending, approved denied etc.) and to get all the unique values from that column I can run the following TSQL query
SELECT DISTINCT STATE FROM CALLOUT_REQUEST
where CALLOUT_REQUEST is my table name and STATE being the field which returns something like:
STATE
approved
denied
pending
...
However I don't understand how I would turn that into a query in my repository as it seems I need a "by" statement or some other filter mechanism which i can get the STATE based on?
What I am looking to return - as shown in the raw TSQL query above - is some kind of List or Array object which contains all the unique/distinct values in all of the STATE fields.
So in pseudo code i think i am looking for something like this:
String[] states = repository.findDisinctState();
where findDistinctState() would then return an array of sorts.
Hope that makes sense - I am very new to Java and Spring in general so I think I am missing some conceptual knowledge to utilise the above.
UPDATE:
The 'state' concept is closed so i could implement that as an enum - only problem is i dont know how to do that :) Ill look into how i can do that as i think it fits perfectly with what i am trying to achieve.
The List i get from the query provided is intended to be used to get a count of all the occurrences. I had this code before to get a total count for each of the 'states':
Map stats = new HashMap();
String[] states = {"approved", "denied", "pending", "deactivated"};
for (int i = 0; i < states.length; i++) {
stats.put(states[i], repository.countByState(states[i]));
}
Am i correct in understanding that the states Array that i have in the above code snippet could be turned into an enum and then i dont even need the custom #Query anymore?
If that state concept is closed - you know its possible set of values - it should be an enum.
After that you can create queries that you invoke like:
repository.findByState(State.APPROVED)
If you can't create an enum, you need a separate method to get the distinct values, which can't be provided by JPA, because you need a list of strings and not a list of CalloutRequests.
Then you need to specify a query manually like:
#Query("SELECT DISTINCT State FROM CALLOUT_REQUEST")
List<String> findDistinctStates();
You can use a JPQL query for this, with the #org.springframework.data.jpa.repository.Query annotation:
#Query("select distinct state from CalloutRequest")
List<String> findDistinctStates();
If you don't want to use #Query then one solution is there to create an interface "StateOnlyInterface" with method named "getState()".
Then create method in your repo with name, getDistinctState(). Return type of this method to be kept as ArrayList of StateOnlyInterface.
I'm using App Engine's Datastore entities in my current project, and I have a multi-valued property for one of the entities. Now, my question is simple, if I store String objects as the values in the multi-value property by passing a String ArrayList as the value in the setProperty("myPropertyName", myArrayList) of my entity, what object will I receive when I run the following:
myEntity.getProperty("myPropertyName");
From my observation it doesn't seem to return an ArrayList, even though ArrayList is a Collection and, according to the documentation, getProperty() returns a Collection object.
The list of supported types can be found here: https://developers.google.com/appengine/docs/java/datastore/entities#Java_Properties_and_value_types.
Strongly consider using a JSON string as GAEfan suggested.
Edit:
According to the OP's comment below, you can store and get multiple values in the datastore as follows:
myEntity.setProperty("myPropertyName", myArrayListOfStrings)
List<String> myValues = myEntity.getProperty("myValueName");
criteria = createCriteria("employee");
criteria.add(Restrictions.eq("name", "John"));
criteria.addOrder(Order.asc("city"));
criteria.addOrder(Order.asc("state"));
List result = criteria.list();
This statement returns a list of Employee objects. How can I make it return a Set of Employee objects instead, in order to remove duplicate data?
I understand I can achieve this by creating a set out of the returned list like below, but then I would lose the sorting order of the list. And I don't want to have to write code to sort the set.
Set<Employee> empSet = new HashSet<Employee>(result);
I don't think it's possible to return a Set using Criteria based on the javadoc. However, if you want to remove duplicate data, why don't add a Projections.distinct(...) to your existing Criteria to remove the duplicates?
http://docs.jboss.org/hibernate/envers/3.6/javadocs/org/hibernate/criterion/Projections.html
UPDATE
For example, if you want to apply a SELECT DISTINCT on the employee name (or some identifier(s)) to get a list of unique employees, you can do something like this:-
List result = session.createCriteria("employee")
.setProjection(Projections.distinct(Projections.property("name")))
.add(Restrictions.eq("name", "John"))
.addOrder(Order.asc("city"))
.addOrder(Order.asc("state"))
.list();
This way, you don't really need to worry about using Set at all.
As the comments and javadoc suggest, you have to return a List from Criteria. Therefore, your only option is to remove uniques after the fact. As KepaniHaole said, you should use a LinkedHashSet if you want to preserve order.
final List <Map<String , Object>> roleList;
final Map<Integer, String> roleMap=new HashMap<Integer, String>();
roleList = getSession()
.createQuery("select Id, Name from Emp where dept=:ref")
.setParameter("ref", "accounts")
.list();
for (Map<String, Object> map2 : roleList)
{
roleMap.put((Integer)(map2.get("Id")), (String)map2.get("Name"));
}
MappingBean.setRoleList(roleMap);
The above code is showing class cast exception [Ljava.lang.Object; cannot be cast to java.util.Map.
Is there any way in Hibernate we can get data in the form of List of Maps? My roleList is in the form of map and I want to set the roleList.
When you make a select like that:
.createQuery("select Id, Name from Emp where dept=:ref")
Hibernate returns by default a List<Object[]>, because that's the safest way to represent a given subset of fields that by no means are bound to have the same type (hence, the lowest common class is considered Object).
You can, however and given the case, transform the result into a map with a simple iteration or through the use of configurations. IMO it seems like an excercise in futility because each record will be returned in a key - Value fashioned way already (even if it is represented as an array of two objects).
Avoid yourself such overhead by doing this, directly (change the type of roleList):
for (Object[] role : roleList)
{
roleMap.put((Integer)role[0]), (String)role[1]);
}
The Object[] returned by Hibernate respects the order of the selected fields so, in your case, index 0 corresponds to Id and 1 to Name.
You could add a ResultTransformer to your query to accomplish this. I am not sure if there is one that does what you want out of the box, though. You may need to write your own. It looks fairly simple, though. I think all you would need would be to extend BasicTransformerAdapter and override the transformTuple method to return a map. This should allow you to control the value returned from query.list() so you can set it directly on MappingBean.