jqwik - Arbitrary Map - Generate a random number of entries within a Map - junit5

This code works to generate a Single Map entry for elements. But I want to generate a random number of entries from within the Map using generateInputMapElements and
pass to the statusReturnedFromApplyingRule()
#Property
//#Report(Reporting.GENERATED)
boolean statusReturnedFromApplyingRule(#ForAll("generateRule") Rule rule,
#ForAll("generateInputMapElements") Iterable<Map<String, Object>> elements) {
RangeMatchRule rangeMatchRule = new RangeMatchRule();
final RuleIF.Status status = rangeMatchRule.applyRule(rule, elements);
return RuleIF.getEnums().contains(status.toString());
}
#Provide
Arbitrary<Iterable<Map<String, Object>>> generateInputMapElements() {
Arbitrary<Double> metricValueArb = Arbitraries.doubles()
.between(0, 50.0);
Arbitrary<Map<String, Object>> inputMapArb =
metricValueArb.map(metricsValue -> {
Map<String, Object> inputMap = new HashMap<>();
inputMap.put(Utils.METRIC_VALUE, metricsValue);
return inputMap;
});
return inputMapArb.map(inputMap -> {
List<Map<String, Object>> inputMapLst = new ArrayList<>();
inputMapLst.add(inputMap);
return inputMapLst;
});
}
How to write a jqwik generator method with nested generators

Assuming that you want a list (iterable) of maps with a single entry, I see two basic options.
Option 1 - Use Arbitrary.list() to generate a list and specify min and max size directly in the generator code:
#Provide
Arbitrary<List<Map<String, Object>>> generateInputMapElements() {
Arbitrary<Double> metricValueArb = Arbitraries.doubles()
.between(0, 50.0);
return metricValueArb
.map(metricsValue -> {
Map<String, Object> inputMap = new HashMap<>();
inputMap.put(Utils.METRIC_VALUE, metricsValue);
return inputMap;
})
.list().ofMinSize(1).ofMaxSize(10);
}
Option 2 - Generate only the individual maps and use standard annotations for the iterable:
#Property
#Report(Reporting.GENERATED)
boolean statusReturnedFromApplyingRule2(
#ForAll("generateRule") Rule rule,
#ForAll #Size(min = 1, max = 10) Iterable<#From("generateInputMap") Map<String, Object>> elements
) {
...
}
#Provide
Arbitrary<Map<String, Object>> generateInputMap() {
Arbitrary<Double> metricValueArb = Arbitraries.doubles()
.between(0, 50.0);
return metricValueArb
.map(metricsValue -> {
Map<String, Object> inputMap = new HashMap<>();
inputMap.put(Utils.METRIC_VALUE, metricsValue);
return inputMap;
});
}
I'd personally go with option 2 because it requires less code. YMMV though.

Related

How in razor core to create a link that has all current parameters plus some more

I'm on a page SomePage?A=a&B=b&...
I want to construct a URL that has all of the current GET parameters plus some more from an IDictionary<string, string> that I have.
The tag helper asp-all-route-data="#myDictionary" will get set the parameters from my dictionary, but I don't understand:
how to create a link with all of the current parameters; or
how to add extra parameters to such a link.
Well this works, but I think it's a bit crap because:
this feels like a really obvious thing to want to so so I don't believe that there isn't an out of the box way to do it,
I can't get the extension method to work -- it has to be called as MakeGet(this, d) rather than just MakeGet(d), and
Shouldn't we be using something like a NameValueCollection that models multiple keys as are supported in GET?
public static IDictionary<string, string> MakeGet<T>(this RazorPage p, IDictionary<string, T> d)
{
return MakeGet(p, d.ToDictionary(z => z.Key, z => { try { return z.ToString(); } catch { return null; } }));
}
public static IDictionary<string, string> MakeGet(this RazorPage p, IDictionary<string, string> d)
{
Dictionary<string, string> result = new Dictionary<string, string>();
foreach (string k in d.Keys)
{
if (!string.IsNullOrWhiteSpace(d[k]))
{
result.Add(k, d[k]);
}
}
IQueryCollection get = p.ViewContext.HttpContext.Request.Query;
foreach (KeyValuePair<string, StringValues> q in p.ViewContext.HttpContext.Request.Query)
{
if (!result.Keys.Contains(q.Key))
{
result.Add(q.Key, string.Join(",", q.Value));
}
}
return result;
}
The next problem is how to subsequently remove a parameter in the controller in order to do a redirect.
public async Task<IActionResult> OnGetTableDeleteAsync()
{
// Need to remove parameter LineNumber.
return RedirectToAction("Get");
}
Well this is what I've come up with for the parameter removal.
public static RouteValueDictionary QueryWithout(this PageModel p, params string[] remove)
{
RouteValueDictionary q = new RouteValueDictionary();
foreach (var kv in (QueryHelpers.ParseQuery(p.Request.QueryString.Value).Where(z => !remove.Contains(z.Key))))
{
q.Add(kv.Key, kv.Value);
}
return q;
}
Being used like this
public async Task<IActionResult> OnGetTableDeleteAsync(int lineNumber)
{
ImportStagingRecord i = _context.ImportStagingRecords.Find(FileId, lineNumber);
if( i != null)
{
_context.ImportStagingRecords.Remove(i);
await _context.SaveChangesAsync();
}
Microsoft.AspNetCore.Routing.RouteValueDictionary q = BaseUri.QueryWithout(this, "LineNumber", "handler");
return RedirectToAction("", q);
}
Again, I think it's crap.
(The query string contains lots of parameters for sorting, filtering, and paging the table of ImportStagingRecords which need to be preserved across requests.)

How to convert a nested list of objects into a map (Map<Integer, Map<Integer, Object>>) using lambdas/streams

I'm converting the following list of objects structure to a HashMap using two for loops as shown below.
public class Vehical {
private Category category;
private List<Brand> brandList;
public Category getCategory() {
return category;
}
public List<Brand> getBrandList() {
return brandList;
}
}
public class Category {
private Integer catId;
public Integer getCatId() {
return catId;
}
}
public class Brand {
private Model model;
public Model getModel() {
return model;
}
}
public class Model {
private List<Reg> regList;
public List<Reg> getRegList() {
return regList;
}
}
public class Reg {
private Integer regId;
public Integer getRegId() {
return regId;
}
}
public static void main(String[] args) {
//Assume that filled with data.***
List<Vehical> vehicalList = getVehicals();
Map<Integer, Map<Integer, Brand>> vehicalMap = new HashMap<Integer, Map<Integer, Brand>>();
for (Vehical vehical : vehicalList) {
Map<Integer, Brand> brandMap = new HashMap<Integer, Brand>();
for (Brand brand : vehical.getBrandList()) {
//Assume that zeroth index is always available and getting "RegId" from the zeroth element is fixed.***
brandMap.put(brand.getModel().getRegList().get(0).getRegId(), brand);
}
vehicalMap.put(vehical.getCategory().getCatId(), brandMap);
}
}
How can I do the same thing using lambdas/streams?
I tried with a flatMap, but it didn't work. Couldn't access the nested RegId while streaming.
Also, I tried with forEach but finally it looks likes the same for loop solution.
Map<Integer, Map<Integer, Brand>> vehicalMap = new HashMap<>();
vehicalList.forEach(v -> {
Map<Integer, Brand> brandMap = new HashMap<>();
v.getBrandList().stream().forEach(b -> brandMap
.put(b.getModel().getRegList().get(0).getRegId(), b));
vehicalMap.put(v.getCategory().getCatId(), brandMap);
});
Any help would be appreciated.
UPDATE
Working code sample based on #Nidhish Krishnan answer
Try this out
SOLUTION 1
If you want to get the result based on grouping, try this one
Map<Integer, Map<Integer, Brand>> vehicalMap = getVehicals().stream()
.collect(Collectors.groupingBy(vechiles -> vechiles.getCategory().getCatId(),
Collectors.collectingAndThen(
Collectors.groupingBy(vechile -> vechile.getBrandList().get(0).getModel().getRegList().get(0).getRegId()),
e ->e.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v -> v.getValue().get(0).getBrandList().get(0)))
)
))
.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
SOLUTION 2
If you don't want to use grouping try the below one
Map<Integer, Map<Integer, Brand>> vehicalMap = getVehicals().stream()
.collect(Collectors.toMap(
vechile -> vechile.getCategory().getCatId(),
vechile -> vechile.getBrandList().stream()
.collect(Collectors.toMap(
brands -> brands.getModel().getRegList().get(0).getRegId(),
brand -> brand, (a, b) -> b)),
(a, b) -> a));
UPDATE 1
Not sure what data you are having for vehicalList, this is how I created the test vehicalList
private static List<Vehical> getVehicals() {
return Lists.newArrayList(
new Vehical(new Category(21), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(100), new Reg(101), new Reg(102)))))),
new Vehical(new Category(22), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(200), new Reg(201), new Reg(202)))))),
new Vehical(new Category(23), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(300), new Reg(301), new Reg(302)))))),
new Vehical(new Category(24), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(400), new Reg(401), new Reg(402)))))),
new Vehical(new Category(25), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(500), new Reg(501), new Reg(502)))))),
new Vehical(new Category(26), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(600), new Reg(601), new Reg(602)))))),
new Vehical(new Category(26), Lists.newArrayList(new Brand(new Model(Lists.newArrayList(new Reg(700), new Reg(701), new Reg(702))))))
);
}
Let say, I have entity class TableColumnInfo
public class TableColumnInfo {
#Column(name = "table_name")
private String tableName;
#Column(name = "column_name")
private String columnName;
#Column(name = "data_type")
private String dataType;
#Column(name = "is_nullable")
private Boolean isNullable;
#Column(name = "column_default")
private String columnDefault;
}
In input, I am given List of Object as below:
List<TableColumnInfo> tableColumnInfoRes = some database repository call;
In output, I want nested MAP as below:
Map<String, Map<String, TableColumnInfo>>
Where:
Outter Map Key: String i.e. tableName
Outter Map Value: Map
Inner Map Key: String i.e. columnName
Inner Map Value: Object i.e. TableColumnInfo
Constraints:
Outter map tableName and Inner map column name must belong to same object.
It means, for every tableName in outter map, inner map must have a column name as a key and entire object as a value (1:1 Mapping)
Solution:
Using Java Functional programming and Collections
Below BiFunction used to generate inner Map
BiFunction<List<TableColumnInfo>, String, Map<String, TableColumnInfo>> mapper1 =
(infoList, tableName2) ->
{ Map<String, TableColumnInfo> res =
infoList.stream().filter(infoList2 -> infoList2.getTableName().equals(tableName2))
.collect(Collectors.toMap(TableColumnInfo::getColumnName, Function.identity()));
return res;
};
Below code used to produce outter map
Map<String, Map<String, TableColumnInfo>> tableColumnMap = tableColumnInfoRes.stream()
.collect(Collectors.toMap(
tableInfo -> tableInfo.getTableName(),
tableInfo -> mapper1.apply(tableColumnInfoRes,tableInfo.getTableName()),
(a,b)->b
));
In above code, BIFunction is called for every table name.
In Inner Map, to produce value as object, make use of Function.identity()
For outter map, to avoid conflict of key, make use of (a,b) -> b
Constraints are satisfied in BiFunction using filters on stream.

Search where A or B with querydsl and spring data rest

http://localhost:8080/users?firstName=a&lastName=b ---> where firstName=a and lastName=b
How to make it to or ---> where firstName=a or lastName=b
But when I set QuerydslBinderCustomizer customize
#Override
default public void customize(QuerydslBindings bindings, QUser user) {
bindings.bind(String.class).all((StringPath path, Collection<? extends String> values) -> {
BooleanBuilder predicate = new BooleanBuilder();
values.forEach( value -> predicate.or(path.containsIgnoreCase(value) );
});
}
http://localhost:8080/users?firstName=a&firstName=b&lastName=b ---> where (firstName=a or firstName = b) and lastName=b
It seem different parameters with AND. Same parameters with what I set(predicate.or/predicate.and)
How to make it different parameters with AND like this ---> where firstName=a or firstName=b or lastName=b ??
thx.
Your current request param are grouped as List firstName and String lastName. I see that you want to keep your request parameters without a binding, but in this case it would make your life easier.
My suggestion is to make a new class with request param:
public class UserRequest {
private String lastName;
private List<String> firstName;
// getters and setters
}
For QueryDSL, you can create a builder object:
public class UserPredicateBuilder{
private List<BooleanExpression> expressions = new ArrayList<>();
public UserPredicateBuilder withFirstName(List<String> firstNameList){
QUser user = QUser.user;
expressions.add(user.firstName.in(firstNameList));
return this;
}
//.. same for other fields
public BooleanExpression build(){
if(expressions.isEmpty()){
return Expressions.asBoolean(true).isTrue();
}
BooleanExpression result = expressions.get(0);
for (int i = 1; i < expressions.size(); i++) {
result = result.and(expressions.get(i));
}
return result;
}
}
And after you can just use the builder as :
public List<User> getUsers(UserRequest userRequest){
BooleanExpression expression = new UserPredicateBuilder()
.withFirstName(userRequest.getFirstName())
// other fields
.build();
return userRepository.findAll(expression).getContent();
}
This is the recommended solution.
If you really want to keep the current params without a binding (they still need some kind of validation, otherwise it can throw an Exception in query dsl binding)
you can group them by path :
Map<StringPath,List<String>> values // example firstName => a,b
and after that to create your boolean expression based on the map:
//initial value
BooleanExpression result = Expressions.asBoolean(true).isTrue();
for (Map.Entry entry: values.entrySet()) {
result = result.and(entry.getKey().in(entry.getValues());
}
return userRepository.findAll(result);

How can I make my lucene search "global" (i.e. for all groups) in liferay?

I use the following in a search container to find the projects in a given group:
SearchContext searchContext = SearchContextFactory.getInstance(request);
searchContext.setStart(searchContainer.getStart());
searchContext.setKeywords(keywords);
searchContext.setEnd(searchContainer.getEnd());
results = ProjectLocalServiceUtil.getSearchedProjects(searchContext);
total = ProjectLocalServiceUtil.getSearchedProjectsCount(searchContext);
The methods getSearchedProjects translates from search results to a list of projects:
public List<Project> getSearchedProjects(SearchContext context) throws SearchException {
Indexer indexer = IndexerRegistryUtil.getIndexer(Project.class);
Hits results = indexer.search(context);
List<Project> projects = new ArrayList<Project>();
for (int i = 0; i < results.getDocs().length; i++) {
com.liferay.portal.kernel.search.Document doc = results.doc(i);
long projectId=GetterUtil.getLong(doc.get(Field.ENTRY_CLASS_PK));
Project project = null;
try {
project = ProjectLocalServiceUtil.getProject(projectId);
projects.add(project);
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
return projects;
}
The results are as I expect when the search portlet is placed in the group referenced in the group I set in my projectIndexer's doGetDocument method:
document.addKeyword(Field.GROUP_ID, groupId);
document.addKeyword(Field.SCOPE_GROUP_ID, groupId);
but I would like to use the portlet to search from another group.
I tried to set the list of groupIds in the portlet, using
searchContext.setGroupIds(new long[] {projectHolder.getGroupId()});
but I still don't get any results in the search.
Any pointers on how I can search 'across groups' ?
Alain
I found the answer to my question. In order for
searchContext.setGroupIds(new long[] {group1ID, group2Id});
to have an effect the searchContext should NOT have groupId as one of its attributes.
SearchContextFactory adds GroupId in the attributes (see the extract below), so after the call to getInstance, you need to remove groupId from the attributes :
SearchContext searchContext = SearchContextFactory.getInstance(request);
Map<String, Serializable> attributes = searchContext.getAttributes();
attributes.remove("groupId");
Hope this helps someone someday.
Alain
Below, the extract from SearchContextFactory.getInstance(HttpServletRequest request) that (I believe - didn't debug it) sets the groupId attribute from the parameters :
// Extract from SearchContextFactory.java
Map<String, String[]> parameters = request.getParameterMap();
for (Map.Entry<String, String[]> entry : parameters.entrySet()) {
String name = entry.getKey();
String[] values = entry.getValue();
if ((values != null) && (values.length > 0)) {
if (values.length == 1) {
attributes.put(name, values[0]);
}
else {
attributes.put(name, values);
}
}
}
searchContext.setAttributes(attributes);

Lucene - How can you modify a Query object's terms?

I want to add new fields to my Lucene-based search engine site, however I want to be able to intercept queries and modify them before I pass them on to the Searcher.
For example each document has the field userid so you can search for documents authored by a particular user by their ID, e.g. foo bar userid:123 however I want to add the ability to search by username.
I'd like to add a field user:RonaldMcDonald to queries (not to documents), however I want to be able to intercept that term and replace it with an equivalent userid:123 term (my own code would be responsible for converting "RonaldMcDonald" to "123").
Here's the simple code I'm using right now:
Int32 get = (pageIndex + 1) * pageSize;
Query query;
try {
query = _queryParser.Parse( queryText );
} catch(ParseException pex) {
log.Add("Could not parse query.");
throw new SearchException( "Could not parse query text.", pex );
}
log.Add("Parsed query.");
TopDocs result = _searcher.Search( query, get );
I've had a look at the Query class, but I can't see any way to retrieve, remove, or insert terms.
You can subclass the QueryParser and override NewTermQuery.
QP qp = new QP("user", new SimpleAnalyzer());
var s = qp.Parse("user:RonaldMcDonald data:[aaa TO bbb]");
Where s is will be userid:123 data:[aaa TO bbb]
public class QP : QueryParser
{
Dictionary<string, string> _dict =
new Dictionary<string, string>(new MyComparer()) {{"RonaldMcDonald","123"} };
public QP(string field, Analyzer analyzer) : base(field, analyzer)
{
}
protected override Query NewTermQuery(Term term)
{
if (term.Field() == "user")
{
//Do your username -> userid mapping
return new TermQuery(new Term("userid", _dict[term.Text()]));
}
return base.NewTermQuery(term);
}
//Case insensitive comparer
class MyComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return String.Compare(x, y, true, CultureInfo.InvariantCulture)==0;
}
public int GetHashCode(string obj)
{
return obj.ToLower(CultureInfo.InvariantCulture).GetHashCode();
}
}
}