How to get parent document immediately followed by child documents - elasticsearch

I am working on hibernate search with the elastic search.I am having a requirement where I need to fetch parent document immediately followed by child documents.
To maintain parent and child relation I am having a table with id ,and parentId column.Here parentId is the id of another record.e.g
Note:For Better understanding run the belowsnippet
<html>
<body>
<table border="1" >
<tr>
<th>ID</th>
<th>Name</th>
<th>ParentId</th>
</tr>
<tr>
<td>1</td>
<td>Samule</td>
<td>0</td>
</tr>
<tr>
<td>2</td>
<td>jhon</td>
<td>1</td>
</tr>
<tr>
<td>3</td>
<td>peeter</td>
<td>2</td>
</tr>
</table>
</body>
</html>
expected output :
for ex: while searching name equals to Samule it needs to fetch in the below order
1
2
Here the first record is the parent record and the second record is the child record.Is there any way to do in elastic search with hibernate search?

The only way I see this possibly happen is if your hierarchy has a known maximum depth. For instance if you know you will only ever have two levels of parents, never three.
If this is your case, you can have a model like this:
#Indexed
public class Document {
#DocumentId
private int id;
#Field
private String text;
#IndexedEmbedded(depth = /* YOUR MAX DEPTH HERE */)
private Document parent;
#Field(name = "path_sort", analyze = Analyze.NO)
public String getPath() {
return parent == null ? id : parent.getPath() + "." + id;
}
}
And perform your search like this:
FullTextSession fts = ... ;
QueryBuilder qb = fts.getSearchFactory()
.buildQueryBuilder()
.forEntity( Document.class )
.get();
Query query = qb.keyword().onFields(
"text",
"parent.text",
"parent.parent.text"
/* and so on, on as many levels as necessary */
)
.matching( "some string" )
.createQuery();
Sort sort = qb.sort().byField( "path_sort" ).createSort();
List<Document> result = fts.createFullTextQuery( query, Document.class )
.setSort( sort )
.list();

Related

Duplicate Keys in Vue v-for Loop When Table is Sorted

My Vue application brings in data from Firestore using vuefire.
I import the data from a the 'lines' collection as follows:
firestore() {
return {
lines: db.collection("lines")
}
}
Each record has a Firestore generated id, which I then use a a key in a v-for loop like:
<thead>
<tr>
<th>Code</th>
<th #click="sort_string(lines,'name')"> Name</th>
<th>Quantity</th>
</tr>
</thead>
<tbody v-for="line in lines" :key="line.id">
<tr>
<td>{{line.code}}</td>
<td>{{line.name}}</td>
<td>{{line.quantity}}</td>
<button #click="pick_one(line)">+1</button>
...
In have a method pick_onewhich changes the quantity on Firestore directly:
pick_one(line) {
const new_quantity = line.quantity + 1;
db
.collection("lines")
.doc(line.id)
.update({ quantity: new_quantity });
}
All of this works fine until I sort() the underlying array ('lines').
If I sort the table and then call the function pick_one I get a duplicate key error:
[Vue warn]: Duplicate keys detected: 'RaTIINFWTQxHQPyRmfsQ'. This may cause an update error.
I can only assume this has something to do with the way that Vuefire handles update() calls, since the act of sorting the array does not cause this problem, only updating a line in the array while it is sorted.
My sort function (vue method) looks like this:
sort_string(table, column) {
console.log("sorting")
this.sort_toggle = -this.sort_toggle;
return table.sort((a, b) => {
if (
a[column].toString().toUpperCase() <
b[column].toString().toUpperCase()
) {
return -this.sort_toggle;
}
if (
a[column].toString().toUpperCase() >
b[column].toString().toUpperCase()
) {
return this.sort_toggle;
}
return 0;
});
},
Is there a way to avoid this behaviour?
Phil's comment provided the clue to this behaviour - in that the sort() function is working on the underlying data rather than a copy.
I have modified my template so that the v-for now loops over a computed array which can be sorted using the (slightly modified) function.
The sort function now uses slice() to create a copy of the underlying array:
computed: {
sorted_lines() {
return sort_string(lines, this.sort_column) // name of the column/filed to sort by
}
The sort_string function now looks like this (with addition of slice()
sort_string(table, column) {
console.log("sorting")
//this.sort_toggle = -this.sort_toggle; // moved elsewhere
return table.slice().sort((a, b) => { // slice() then sort()
if (
a[column].toString().toUpperCase() <
b[column].toString().toUpperCase()
) {
return -this.sort_toggle;
}
if (
a[column].toString().toUpperCase() >
b[column].toString().toUpperCase()
) {
return this.sort_toggle;
}
return 0;
});
},

Spring Data JPA, dynamic sorting based upon possible entry in nested collection

As the codebase is already using Spring Data JPA, I would like to create a Sort
object, which will be based upon the presence (or lack thereof) of a particular element
being present in a Collection, collection itself which is one of the element in the
primary table. The Sort object's property would need to be dynamic, as a user might want to sort
records once one way and the next time another way.
Explicitly, if multiple PrimaryEntity objects have a SecondaryEntity with 'type' set to a particular
value, I would then want to sort them based upon the corresponding 'notes' field in the corresponding
SecondaryEntity. Also, while I would want to retrieve all SecondaryEntity objects, I would want the
sorting to be based solely upon the SecondaryEntity records where 'type' is equal to, say, 'Important'.
The classes look like the following (I also redefined 'equals' & 'hashCode' for SecondaryEntity):
public class PrimaryEntity
{
#OneToMany(mappedBy = "primary", cascade = CascadeType.ALL)
#MapKey(name = "type")
private Map<String, SecondaryEntity> myMap = new HashMap<>();
#Column(name = "name")
private String name;
}
public class SecondaryEntity
{
#Column(name = "type", length = 200)
private String type;
#Column(name = "notes", length = 2000)
private String notes;
#ManyToOne
#JoinColumn(name = "primary_id", referencedColumnName = "id")
private PrimaryEntity primary;
}
I would then want to create a Sort with a syntax similar to he following:
Sort sort = new Sort("myMap[important].notes")
Finally, while I am striving to sort the PrimaryEntity records as per above, it does not matter to
me how, for a given PrimaryEntity, its SecondaryEntity records are displayed.
For example,
<html>
<head>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
</style>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>Type</th>
<th>Notes</th>
</tr>
<tr>
<td>Second primary</td>
<td>Important</td>
<td>1</td>
</tr>
<tr>
<td>Second primary</td>
<td>Other</td>
<td>2</td>
</tr>
<tr>
<td>Second primary</td>
<td>Miscellaneous</td>
<td></td>
</tr>
<tr>
<td>Third primary</td>
<td>Important</td>
<td>2</td>
</tr>
<tr>
<td>First primary</td>
<td>Important</td>
<td>3</td>
</tr>
</table>
</body>
</html>
Thank you.
You can optionally override findAll(Sort sort) method in the SecondaryRepo with #EntityGraph annotation:
public interface SecondaryRepo extends JpaRepository<Secondary, Long> {
#EntityGraph(attributePaths = {"primary"})
#Override
List<Secondary> findAll(Sort sort);
}
to fetch associated Primaries eagerly.
Then just define Sort with type and note and fetch Secondaries (that will contain also their Primaries) like this:
Sort sort = new Sort(new Sort.Order("type"), new Sort.Order("note"));
List<Secondary> list = secondaryRepo.findAll(sort);
See example and test.

TemplateProcessingException: Nested variables, map and foreach loop

I have the following models,
public class Shift {
private UUID id;
private UUID unit;
private List employees;
private Timestamp startTime;
private Timestamp endTime;
...
}
public class Unit {
private UUID id;
private String name;
...
}
following route,
path("/shift", () -> {
get("", ShiftController.fetchShifts);
});
following controller,
public static Route fetchShifts = (Request req, Response res) -> {
Map map = new HashMap<>();
map.put("shifts", shiftDao.findAllByOrderByUnitAscStartTimeAsc());
map.put("units", unitDao.findAllByOrderByName().stream().collect(Collectors.toMap(Unit::getId, u -> u)));
return render(req, map, "shifts");
};
following template,
<table>
<tbody>
<tr th:each="s : ${shifts}">
<td th:text="*{units[__${s.unit}__].name}">unit</td>
</tr>
</tbody>
</table>
which gives me,
ERROR org.thymeleaf.TemplateEngine - [THYMELEAF][qtp1905797065-18] Exception processing template "shifts": Exception evaluating OGNL expression: "units[dd002ece-10c7-11e7-9009-93b58da4760f].name"
...
org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating OGNL expression: "units[dd002ece-10c7-11e7-9009-93b58da4760f].name"
...
Caused by: ognl.ExpressionSyntaxException: Malformed OGNL expression: units[dd002ece-10c7-11e7-9009-93b58da4760f].name [ognl.ParseException: Encountered " "c7 ""
...
and for the death of me I can't figure out the problem. What I want is to iterate through all the shifts and find out the name of the unit for each shift. For this I create a map units in the controller with the ids of units and objects representing them. However, I'm unsuccessful in implementing the map in the template. *{units.get(__${s.unit}__).name} in the template gives similar errors.
It should look like this:
<table>
<tbody>
<tr th:each="s: ${shifts}">
<td th:text="${units.get(s.unit).name}" />
</tr>
</tbody>
</table>
You have a few problems with your thymeleaf.
As the error message states, units[dd002ece-10c7-11e7-9009-93b58da4760f].name is not a valid expression. As far as I know, you can only use the ${map[index]} expression with numbers (which look like map[0]) and strings (which look like map['test']). Your expression is neither -- to the parser, you have a string missing the containing quotes.
Second, you're misusing __${}__ expressions. You should really only need to ever use __${}__ when you are defining a th:field expression. In most other cases, you should be able to do everything without them.

Subtract dates in Linq EF6

Hello i have big problem with Subtract at Linq,EF6.
I have date where repair should be finieshed. I woluld like to count how many days left.
At ViewModel I have:
public TimeSpan TimeToLeft{get;set;}
At repair controler i do sth like this:
var repairsToDo = from r in db.Repairs
join c in db.Car on r.Car equals c.ID_Car
join m in db.Models on c.ID_Modelu equals m.ID_Modelu
join b in db.Brand on m.ID_Brand equals b.Brand
where r.Data_Zakonczenia>=DateTime.Today
select new RepairsToDo { TimeToLeft=(r.EndDate-DateTime.Today) };
View:
<table class="ShowDataTab">
<tr>
<th>Repair Number</th>
<th>Car</th>
<th>Name</th>
<th>Desc</th>
<th>Time to left</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>#item.FixNumber</td>
<td>#item.Brand#item.Model</td>
<td>#item.FixName</td>
<td>#item.FixDesc</td>
<td>#item.TimeToLeft</td>
</tr>
}
</table>
And i got error like this:
dbarithmeticexpression arguments must have a numeric common type
How can i Fix it?
EDIT1:
Controler:
var today = DateTime.Today;
var repairsToDo = from r in db.Repair
join c in db.Car on r.Car equals c.ID_Car
join m in db.Models on c.ID_Model equals m.ID_Model
join b in db.Brand on m.ID_Brand equals b.ID_Brand
where r.EndTime>=DateTime.Today
select new { ... EndTime=r.EndTime };
var model = repairsToDo.AsEnumerable().Select(raw => new RepairsToDo {... TimeLeft= raw.EndTime- today });
return View(model);
Error:
The model item passed into the dictionary is of type 'System.Linq.Enumerable+WhereSelectEnumerableIterator`2[<>f__AnonymousType1a`7[System.Int32,System.String,System.String,System.String,System.String,System.String,System.DateTime],Praca_Inzynierska.Models.RepairsToDo]', but this dictionary requires a model item of type 'System.Linq.IQueryable`1[Praca_Inzynierska.Models.RepairsToDo]'.
enter code here
It's probably simplest to just fetch the data from EF, and then perform the arithmetic locally:
var today = DateTime.Today;
var rawData = from r in db.Repairs
join c in db.Car on r.Car equals c.ID_Car
join m in db.Models on c.ID_Modelu equals m.ID_Modelu
join b in db.Brand on m.ID_Brand equals b.Brand
where r.Data_Zakonczenia >= DateTime.Today
select new { ..., r.EndDate };
var model = rawData.AsEnumerable() // Perform the select locally
.Select(raw => new RepairsToDo {
... // other properties
TimeToLeft = raw.EndDate - today
});
Note that I've fetched DateTime.Today once, rather than doing it multiple times - that way you'll get a consistent result, even if this query is performed around midnight.
I'd also recommend renaming TimeToLeft as TimeLeft or RemainingTime.
Try:
TimeToLeft = SqlFunctions.DateDiff("DAY", r.EndDate, DateTime.Now)
Change DAY for whatever unit you want. See http://msdn.microsoft.com/en-us/library/dd487052(v=vs.110).aspx and http://msdn.microsoft.com/en-us/library/ms189794.aspx.
With EF6 use
System.Data.Entity.DbFunctions.DiffHours(time1,time2).Value
for example:
using System.Data.Entity;
...
entity.tableData.Select(m => new
{
m.Key,
horasTotales = m.Sum(h => DbFunctions.DiffHours(h.fecha_fin, h.fecha_inicio).Value)
})

How I can dynamically add a new entity to my Htmlhelper.dropdownlist

I have the following action method that initiated SelectList as follow:-
public PartialViewResult Search(string q, int q2,string q3,string sortOrder)
{
ViewBag.q2 = new SelectList(elearningrepository.FindAllLevels().ToList(), "DifficultyID", "Description", 2);
ViewBag.q3 = new SelectList(elearningrepository.FindAllusers().ToList(), "UserID", "UserID",2);
ViewBag.today = DateTime.Today;
ViewBag.nextmonth = DateTime.Now.AddMonths(1);
var v = elearningrepository.searchquestions3(q, q2, q3);
return PartialView("_searchquestion", v);
}
And the following repository method:-
public IQueryable<Question> searchquestions3(string q, int? q2 , string)
{
return from u in entities1.Questions
where (u.Description.Contains(q) && (u.DifficultyID == q2 || q2 == "Any" ) && ( u.CreatedBy == q3 || q3 == "Any"))
select u;}
then on the view i render the drop down list as follow:-
<th>
#Html.DropDownList("q2")
</th>
</tr>
<tr>
<th>
Seach By Create By:-
</th>
<th>
#Html.DropDownList("q3")
</th>
But how i can add a new value that represents “Any” word to the q2 & q3 drop down lists, so that the repository method will work as intended ?
BR
First of all I would advice you to use ViewModels when you work with more complex objects. You can achieve it with the ViewBag of course, but it's messy.
Now. In the action you can do:
var list = elearningrepository.FindAllLevels().ToList()
list.InsertAt(0, new TypeOfYourObject() {ValueProperty = "Any"});
ViewBag.q2 = new SelectList(list, "DifficultyID", "Description", 2);
You can do adequatly with the others, but seriously think about strongly typed ViewModel.
#Html.DropDownList("q3","Any")
You need to use the optionLabel
in your controller that you get your post.
Whatever you want to then do, example. a database transaction, do only if the value == empty string or label == "Any"
Client side solution with Jquery
$("#Select_Id").append("<option value='0'>Text</option>");
You can use .prepend() if you want to add it as first element.

Resources