Gatling: Map fields from Json Response to a Map Object - gatling

My response looks like this
{
"data":[
{
"id":"919274ee42fe01d40f89f51239009a2b",
"descriptor":"Long Count_copy_1938",
"alias":"longCount_Copy_1938",
"type":"Numeric"
},
{
"id":"435274ee42fe01d40f89f51239009a2b",
"descriptor":"Long Count2",
"alias":"longCount_Copy2",
"type":"Numeric"
},
{
"id":"345274ee42fe01d40f89f51239009a2b",
"descriptor":"Short Count2",
"alias":"Short count",
"type":"Numeric"
}
]
}
I would like to extract "descriptor":"id" to a Map. After mapping, the Map object should look like
"Long Count_copy_1938" -> "919274ee42fe01d40f89f51239009a2b"
"Long Count2" -> "435274ee42fe01d40f89f51239009a2b"
"Short Count2" -> "345274ee42fe01d40f89f51239009a2b"
Here is how I am achieving it, Let me know if there is a better way. Thanks!
exec(http("Get Field ids")
.get(s"${wqlDataSources}/")
.check(status.is(200),
jsonPath("$.data[*].descriptor").findAll.saveAs("descriptors"),
jsonPath("$.data[*].id").findAll.saveAs("ids")))
.exec(session => {
val descriptors = session("descriptors").as[Vector[String]]
val ids = session("ids").as[Vector[String]]
val fieldIdMap = (descriptors zip ids).toMap
session.set("fieldIdResultMap", fieldIdMap)
session.remove("descriptors").remove("ids")
})

Most JSONPath implementations support to extract multiple values in one go using the [,] union operator, e.g. $..['id','descriptor'] to matches your two properties.
(However, the availability and the result of the union is neither universal nor consistent, if you check the above path online here by switching to Goessner or Jayway you will notice the result are not the same, the Gattling tab on the test site even throws an error; I cannot tell if it would work if the site would use the latest version.)
I could not find any documentation that confirmed Gatling supports unions but I found this test on Gatling's Github repo that has a union: JsonPath.query("$.menu['file','year']", json) ... So, unioning should work, in general.
With some trial and error, I found this path that works using Gatling (even with older versions):
$.data[*]['id','descriptor']
Which returns:
[
"919274ee42fe01d40f89f51239009a2b",
"Long Count_copy_1938",
"435274ee42fe01d40f89f51239009a2b",
"Long Count2",
"345274ee42fe01d40f89f51239009a2b",
"Short Count2"
]
So, you should be able to map the key/value pairs in one go using this path!

Here is 1 more solution that worked,
exec(http("Get Field ids for the data source")
.get(s"${wqlDataSources}/" + "${datasourceId}/fields)
.check(status.is(200),
jsonPath("$.data[*].descriptor").findAll.saveAs("descriptors"),
jsonPath("$.data[*].id").findAll.saveAs("ids")))
.exec(session => {
val descriptors = session("descriptors").as[Vector[String]]
val ids = session("ids").as[Vector[String]]
val currentFieldIdMap = (descriptors zip ids).toMap
})

Related

Cannot return documents based off a sorted index using Fauna DB

I'm bumbling my way through adding a back-end to my site and have decided to get acquainted with graphQL. I may be structuring things totally the wrong way, however from following some tutorials I have a React front-end (hosted on Vercel), so I have created an api folder in my app to make use of Vercel's serverless functions. I'm using Apollo server and I decided to go with Fauna as my database.
I've successfully been able to return an entire collection via my API. Now I wish to be able to return the collection sorted by my id field.
To do this I created an index which looks like this:
{
name: "sort_by_id",
unique: false,
serialized: true,
source: "my_first_collection",
values: [
{
field: ["data", "id"]
},
{
field: ["ref"]
}
]
}
I then was able to call this via my api and get back and array, which simply contained the ID + ref, rather than the associated documents. I also could only console log it, I assume because the resolver was expecting to be passed an array of objects with the same fields as my typedefs. I understand I need to use the ref in order to look up the documents, and here is where I'm stuck. An index record looks as follows:
[1, Ref(Collection("my_first_collection"), "352434683448919125")]
In my resolvers.js script, I am attempting to receive the documents of my sorted index list. I've tried this:
async users() {
const response = await client.query(
q.Map(
q.Paginate(
q.Match(
q.Index('sort_by_id')
)
),
q.Lambda((ref) => q.Get(ref))
)
)
const res = response.data.map(item => item.data);
return [... res]
}
I'm unsure if the problem is with how I've structured my index, or if it is with my code, I'd appreciate any advice.
It looks like you also asked this question on the Fauna discourse forums and got an answer there: https://forums.fauna.com/t/unable-to-return-a-list-of-documents-via-an-index/3511/2
Your index returns a tuple (just an array in Javascript) of the data.id field and the ref. You confirmed that with your example result
[
/* data.id */ 1,
/* ref */ Ref(Collection("my_first_collection"), "352434683448919125")
]
When you map over those results, you need to Get the Ref. Your query uses q.Lambda((ref) => q.Get(ref)) which passes the whole tuple to Get
Instead, use:
q.Lambda(["id", "ref"], q.Get(q.Var("ref")))
// or with JS arrow function
q.Lambda((id, ref) => q.Get(ref))
or this will work, too
q.Lambda("index_entry", q.Get(q.Select(1, q.Var("index_entry"))))
// or with JS arrow function
q.Lambda((index_entry) => q.Get(q.Select(1, index_entry)))
The point is, only pass the Ref to the Get function.

DotNet Core Azure Search SDK - filtering results

We are trying to implement Filter functionality into Azure (Cognitive) Search. I was hoping to find some nice SDK methods that hide all the ugly parts, but so far the only example I found looks like this (source):
SearchParameters parameters = new SearchParameters()
{
Filter = String.Format("groupIds/any(p:search.in(p, '{0}'))", string.Join(",", groups.Select(g => g.ToString()))),
Select = new[] { "application essays" }
};
I was wondering, whether I am missing some docs. Or maybe it is on the roadmap?
Check out our new Azure.Search.Documents SDK we released last month. It does have OData filter helps as you can find here:
int stars = 4;
SearchOptions options = new SearchOptions
{
// Filter to only Rating greater than or equal our preference
Filter = SearchFilter.Create($"Rating ge {stars}"),
Size = 5, // Take only 5 results
OrderBy = { "Rating desc" } // Sort by Rating from high to low
};
It'll escape string parameters correctly. The OData $filter syntax still requires raw input, but the type helpers in the formattable string should make your situation easier: you don't have to worry about escaping values yourself.

building request body from optional checks

I'm trying to model a scenario where an initial request will provide several different lists of Ids, then a later request needs to submit the values in these lists (if they exist) as part of it's JSON payload. None of the lists is guaranteed to be in the response, and if it isn't, then there is no corresponding entry at all for it in the later JSON.
For example, call 1 might return 3 different lists of Ids that can be saved for later calls...
.check(
jsonPath("$..manager").findAll.optional.saveAs("managemerIds"),
jsonPath("$..employee").findAll.optional.saveAs("employeeIds"),
jsonPath("$..temp").findAll.optional.saveAs("tempIds")
)
Later I need to submit these as a request body formatted like
{"managers":"${managerIds.jsonStringify()}",
"employees":"${employeeIds.jsonStringify()}",
"temps":"${tempIds.jsonStringify()}"}
but if one of the lists of Ids is empty, then it can't be submitted in the JSON at all - ie: if there were no tempId's from the first request, then the JSON payload needs to look like
{"managers":"${managerIds.jsonStringify()}",
"employees":"${employeeIds.jsonStringify()}"}
I could get this working by using a transformOption on the check to set the session variable to an empty list and then have conditional building of the JSON payload by doing something like
jsonPath("$..temp").findAll.transformOption(ids => ids.orElse(Some(Seq.empty[String])).success).saveAs("tempIds"))
but I was hoping for something more scala idiomatic using Options.
I can generate the required JSON for the body this way with something like
private def createPayload(
managers: Option[String] = None,
employees: Option[String] = None,
temps: Option[String] = None) : String = {
Map("managers" -> managers,
"employees" -> employees,
"temps" -> temps
).foldLeft(JsObject.empty){ case (result, (key, values)) => {
values match {
case Some(ids) => result ++ result.transform((__ \ key).json.put(JsString(ids))).get
case None => result
}
}}.toString()
but I can't work out a way to just pass the option that results from attempting to resolve the session variable
so I've come up with a solution that seems to work pretty well - I just had to resolve some session variables in the StringBody function and build the JSON according to requirements.
using the play framework's JSON capabilities is probably overkill though
def contactPeople(managers: Expression[Seq[String]], employees: Expression[Seq[String]], temps: Expression[Seq[String]]: ChainBuilder = {
exec(http(...)
.post(...)
.headers(...)
.body(StringBody(session =>
Map("managers" -> managers, "employees" -> employees, "temps" -> temps)
.foldLeft(JsObject.empty){ case (result, (key, values)) => {
values.apply(session) match {
case Success(ids) => result ++ result.transform((__ \ key).json.put(Json.toJson[Seq[String]](ids))).get
case Failure(error) => result
}
}}.toString
))
...
}

Issue with .populate() on array of arrays in Mongoose Model [duplicate]

In Mongoose, I can use a query populate to populate additional fields after a query. I can also populate multiple paths, such as
Person.find({})
.populate('books movie', 'title pages director')
.exec()
However, this would generate a lookup on book gathering the fields for title, pages and director - and also a lookup on movie gathering the fields for title, pages and director as well. What I want is to get title and pages from books only, and director from movie. I could do something like this:
Person.find({})
.populate('books', 'title pages')
.populate('movie', 'director')
.exec()
which gives me the expected result and queries.
But is there any way to have the behavior of the second snippet using a similar "single line" syntax like the first snippet? The reason for that, is that I want to programmatically determine the arguments for the populate function and feed it in. I cannot do that for multiple populate calls.
After looking into the sourcecode of mongoose, I solved this with:
var populateQuery = [{path:'books', select:'title pages'}, {path:'movie', select:'director'}];
Person.find({})
.populate(populateQuery)
.execPopulate()
you can also do something like below:
{path:'user',select:['key1','key2']}
You achieve that by simply passing object or array of objects to populate() method.
const query = [
{
path:'books',
select:'title pages'
},
{
path:'movie',
select:'director'
}
];
const result = await Person.find().populate(query).lean();
Consider that lean() method is optional, it just returns raw json rather than mongoose object and makes code execution a little bit faster! Don't forget to make your function (callback) async!
This is how it's done based on the Mongoose JS documentation http://mongoosejs.com/docs/populate.html
Let's say you have a BookCollection schema which contains users and books
In order to perform a query and get all the BookCollections with its related users and books you would do this
models.BookCollection
.find({})
.populate('user')
.populate('books')
.lean()
.exec(function (err, bookcollection) {
if (err) return console.error(err);
try {
mongoose.connection.close();
res.render('viewbookcollection', { content: bookcollection});
} catch (e) {
console.log("errror getting bookcollection"+e);
}
//Your Schema must include path
let createdData =Person.create(dataYouWant)
await createdData.populate([{path:'books', select:'title pages'},{path:'movie', select:'director'}])

Using CouchDB-lucene how can I index an array of objects (not values)

Hello everyone and thanks in advance for any ideas, suggestions or answers.
First, the environment: I am using CouchDB (currently developing on 1.0.2) and couchdb-lucene 0.7. Obviously, I am using couchdb-lucene ("c-l" hereafter) to provide full-text searching within couchdb.
Second, let me provide everyone with an example couchdb document:
{
"_id": "5580c781345e4c65b0e75a220232acf5",
"_rev": "2-bf2921c3173163a18dc1797d9a0c8364",
"$type": "resource",
"$versionids": [
"5580c781345e4c65b0e75a220232acf5-0",
"5580c781345e4c65b0e75a220232acf5-1"
],
"$usagerights": [
{
"group-administrators": 31
},
{
"group-users": 3
}
],
"$currentversionid": "5580c781345e4c65b0e75a220232acf5-1",
"$tags": [
"Tag1",
"Tag2"
],
"$created": "/Date(1314973405895-0500)/",
"$creator": "administrator",
"$modified": "/Date(1314973405895-0500)/",
"$modifier": "administrator",
"$checkedoutat": "/Date(1314975155766-0500)/",
"$checkedoutto": "administrator",
"$lastcommit": "/Date(1314973405895-0500)/",
"$lastcommitter": "administrator",
"$title": "Test resource"
}
Third, let me explain what I want to do. I am trying to figure out how to index the '$usagerights' property. I am using the word index very loosely because I really do not care about being able to search it, I simply want to 'store' it so that it is returned with the search results. Anyway, the property is an array of json objects. Now, these json objects that compose the array will always have a single json property.
Based on my understanding of couchdb-lucene, I need to reduce this array to a comma separated string. I would expect something like "group-administrators:31,group-users:3" to be a final output.
Thus, my question is essentially: How can I reduce the $usagerights json array above to a comma separated string of key:value pairs within the couchdb design document as used by couchdb-lucene?
A previous question I posted regarding indexing of tagging in a similar situation, provided for reference: How-to index arrays (tags) in CouchDB using couchdb-lucene
Finally, if you need any additional details, please just post a comment and I will provide it.
Maybe I am missing something, but the only difference I see from your previous question, is that you should iterate on the objects. Then the code should be:
function(doc) {
var result = new Document(), usage, right;
for(var i in doc.$usagerights) {
usage = doc.$usagerights[i];
for(right in usage) {
result.add(right + ":" + usage[right]);
}
}
return result;
}
There's no requirement to convert to a comma-separated list of values (I'd be intrigued to know where you picked up that idea).
If you simply want the $usagerights item returned with your results, do this;
ret.add(JSON.stringify(doc.$usagerights),
{"index":"no", "store":"yes", "field":"usagerights"});
Lucene stores strings, not JSON, so you'll need to JSON.parse the string on query.

Resources