Rally Lookback: help fetching all history based on future state - rally

Probably a lookback newbie question, but how do I return all of the history for stories based on an attribute that gets set later in their history?
Specifically, I want to load all of the history for all stories/defects in my project that have an accepted date in the last two weeks.
The following query (below) doesn't work because it (of course) only returns those history records where accepted date matches the query. What I actually want is all of the history records for any defect/story that is eventually accepted after that date...
filters :
[
{
property: "_TypeHierarchy",
value: { $nin: [ -51009, -51012, -51031, -51078 ] }
},
{
property: "_ProjectHierarchy",
value: this.getContext().getProject().ObjectID
},
{
property: "AcceptedDate",
value: { $gt: Ext.Date.format(twoWeeksBack, 'Y-m-d') }
}
]

Thanks to Nick's help, I divided this into two queries. The first grabs the final history record for stories/defects with an accepted date. I accumulate the object ids from that list, then kick off the second query, which finds the entire history for each object returned from the first query.
Note that I'm caching some variables in the "window" scope - that's my lame workaround to the fact that I can't ever quite figure out the context of "this" when I need it...
window.projectId = this.getContext().getProject().ObjectID;
I also end up flushing window.objectIds (where I store the results from the first query) when I exec the query, so I don't accumulate results across reloads. I'm sure there's a better way to do this, but I struggle with scope in javascript.
filter for first query
filters : [ {
property : "_TypeHierarchy",
value : {
$nin : [ -51009, -51012, -51031, -51078 ]
}
}, {
property : "_ProjectHierarchy",
value : window.projectId
}, {
property : "AcceptedDate",
value : {
$gt : Ext.Date.format(monthBack, 'Y-m-d')
}
}, {
property : "_ValidTo",
value : {
$gt : '3000-01-01'
}
} ]
Filter for second query:
filters : [ {
property : "_TypeHierarchy",
value : {
$nin : [ -51009, -51012, -51031, -51078 ]
}
}, {
property : "_ProjectHierarchy",
value : window.projectId
}, {
property : "ObjectID",
value : {
$in : window.objectIds
}
}, {
property : "c_Kanban",
value : {
$exists : true
}
} ]

Here's an alternative query that will return only the snapshots that represent transition into the Accepted state.
find:{
_TypeHierarchy: { $in : [ -51038, -51006 ] },
_ProjectHierarchy: 999999,
ScheduleState: { $gte: "Accepted" },
"_PreviousValues.ScheduleState": {$lt: "Accepted", $exists: true},
AcceptedDate: { $gte: "2014-02-01TZ" }
}
A second query is still required if you need the full history of the stories/defects. This should at least give you a cleaner initial list. Also note that Project: 999999 limits to the given project, while _ProjectHierarchy finds stories/defects in the child projects, as well.
In case you are interested, the query is similar to scenario #5 in the Lookback API documentation at https://rally1.rallydev.com/analytics/doc/.

If I understand the question, you want to get stories that are currently accepted, but you want that the returned results include snapshots from the time when they were not accepted. Before you write code, you may test an equivalent query in the browser and see if the results look as expected.
Here is an example - you will have to change OIDs.
https://rally1.rallydev.com/analytics/v2.0/service/rally/workspace/12352608129/artifact/snapshot/query.js?find={"_ProjectHierarchy":12352608219,"_TypeHierarchy":"HierarchicalRequirement","ScheduleState":"Accepted",_ValidFrom:{$gte: "2013-11-01",$lt: "2014-01-01"}}},sort:[{"ObjectID": 1},{_ValidFrom: 1}]&fields=["Name","ScheduleState","PlanEstimate"]&hydrate=["ScheduleState"]
You are correct that a query like this: find={"AcceptedDate":{$gt:"2014-01-01T00:00:00.000Z"}}
will return one snapshot per story that satisfies it.
https://rally1.rallydev.com/analytics/v2.0/service/rally/workspace/12352608129/artifact/snapshot/query.js?find={"AcceptedDate":{$gt:"2014-01-01T00:00:00.000Z"}}&fields=true&start=0&pagesize=1000
but a query like this: find={"ObjectID":{$in:[16483705391,16437964257,14943067452]}}
will return the whole history of the 3 artifacts:
https://rally1.rallydev.com/analytics/v2.0/service/rally/workspace/12352608129/artifact/snapshot/query.js?find={"ObjectID":{$in:[16483705391,16437964257,14943067452]}}&fields=true&start=0&pagesize=1000
To illustrate, here are some numbers: the last query returns 17 results for me. I check each story's revision history, and the number of revisions per story are 5, 5, 7 respectively, sum of which is equal to the total result count returned by the query.
On the other hand the number of stories that meet find={"AcceptedDate":{$gt:"2014-01-01T00:00:00.000Z"}} is 13. And the query based on the accepted date returns 13 results, one snapshot per story.

Related

Is there a way to use the graphLookup aggregation pipeline stage for arrays?

I am currently working on an application that uses MongoDB as the data repository. I am mainly concerned about the graphLookup query to establish links between different people, based on what flights they took. My document contains an array field, that in turn contains key value pairs. I need to establish the links based on one of the key:value pairs of that array.
I have already tried some queries of aggregation pipeline with $graphLookup as one of the stages and they have all worked fine. But now that I am trying to use it with an array, I am hitting a blank.
Below is the array field from the first document :
"movementSegments":[
{
"carrierCode":"MO269",
"departureDateTimeMillis":1550932676000,
"arrivalDateTimeMillis":1551019076000,
"departurePort":"DOH",
"arrivalPort":"LHR",
"departurePortText":"HAMAD INTERNATIONAL AIRPORT",
"arrivalPortText":"LONDON HEATHROW",
"serviceNameText":"",
"serviceKey":"BA007_1550932676000",
"departurePortLatLong":"25.273056,51.608056",
"arrivalPortLatLong":"51.4706,-0.461941",
"departureWeeklyTemporalSpatialWindow":"DOH_8",
"departureMonthlyTemporalSpatialWindow":"DOH_2",
"arrivalWeeklyTemporalSpatialWindow":"LHR_8",
"arrivalMonthlyTemporalSpatialWindow":"LHR_2"
}
]
The other document has the below field :
"movementSegments":[
{
"carrierCode":"MO269",
"departureDateTimeMillis":1548254276000,
"arrivalDateTimeMillis":1548340676000,
"departurePort":"DOH",
"arrivalPort":"LHR",
"departurePortText":"HAMAD INTERNATIONAL AIRPORT",
"arrivalPortText":"LONDON HEATHROW",
"serviceNameText":"",
"serviceKey":"BA003_1548254276000",
"departurePortLatLong":"25.273056,51.608056",
"arrivalPortLatLong":"51.4706,-0.461941",
"departureWeeklyTemporalSpatialWindow":"DOH_4",
"departureMonthlyTemporalSpatialWindow":"DOH_1",
"arrivalWeeklyTemporalSpatialWindow":"LHR_4",
"arrivalMonthlyTemporalSpatialWindow":"LHR_1"
},
{
"carrierCode":"MO270",
"departureDateTimeMillis":1548254276000,
"arrivalDateTimeMillis":1548340676000,
"departurePort":"DOH",
"arrivalPort":"LHR",
"departurePortText":"HAMAD INTERNATIONAL AIRPORT",
"arrivalPortText":"LONDON HEATHROW",
"serviceNameText":"",
"serviceKey":"BA003_1548254276000",
"departurePortLatLong":"25.273056,51.608056",
"arrivalPortLatLong":"51.4706,-0.461941",
"departureWeeklyTemporalSpatialWindow":"DOH_4",
"departureMonthlyTemporalSpatialWindow":"DOH_1",
"arrivalWeeklyTemporalSpatialWindow":"LHR_4",
"arrivalMonthlyTemporalSpatialWindow":"LHR_1"
}
]
And I am running the below query :
db.person_events.aggregate([
{ $match: { eventId: "22446688" } },
{
$graphLookup: {
from: 'person_events',
startWith: '$movementSegments.carrierCode',
connectFromField: 'carrierCode',
connectToField: 'carrierCode',
as: 'carrier_connections'
}
}
])
The above query creates an array field in the document, but there are no values in it. As per the expectation, both my documents should get linked based on the carrier number.
Just to be clear about the query, the documents contain an eventId field, and the match pipeline returns one document to me after the match stage.
Well, I don't know how I missed it, but here is the solution to my problem which gives me the required results :
db.person_events.aggregate([
{ $match: { eventId: "22446688" } },
{
$graphLookup: {
from: 'person_events',
startWith: '$movementSegments.carrierCode',
connectFromField: 'movementSegments.carrierCode',
connectToField: 'movementSegments.carrierCode',
as: 'carrier_connections'
}
}
])

How can i get the _ValidFrom field of Previous record when using lookback API

I'm performing the following lookback snapshot. I got the data back but i also need the ValidFrom Date of the Completed record. It's not a part of the _PreviousValues record. How can i get that to come back with my query?
Thanks!
find: {
'_TypeHierarchy': 'HierarchicalRequirement',
'Children':null,'ScheduleState':'Accepted',
'_PreviousValues.ScheduleState':'Completed',
'_ValidFrom': { '$gte':startDate},
'_ValidTo': { '$lte': endDate},
},
fetch: ['FormattedID','Name','_ValidFrom','_ValidTo','BlockedReason','_User','WorkProduct','ScheduleState','_PreviousValues.ScheduleState','AcceptedDate'],
// order: 'OpenedDate DESC',
hydrate: ['FormattedID','Name','_ValidFrom','_ValidTo','BlockedReason','_User','WorkProduct','ScheduleState','_PreviousValues.ScheduleState','AcceptedDate'],
compress: true,
It looks like what we need is something like _PreviousValues.ScheduleState._ValidFrom, but it does not exist.
I think it is not possible to get _ValidFrom value of the _PreviousValues.ScheduleState from the same query, and a separate query is needed.
For example, this query:
https://rally1.rallydev.com/analytics/v2.0/service/rally/workspace/1234/artifact/snapshot/query.js?find={"Project":5678,"_TypeHierarchy":"HierarchicalRequirement","ScheduleState":"Accepted", "_PreviousValues.ScheduleState": "Completed"}&fields=["ObjectID","_ValidFrom","_ValidTo","ScheduleState","_PreviousValues.ScheduleState"]&hydrate=["ScheduleState","_PreviousValues.ScheduleState"]&compress=true
will return _PreviousValues object which only includes state value:
_PreviousValues: {
ScheduleState: "Completed"
}
Let's say one of the results has ObjectID 777.
The second query will use ObjectID(s) of the results of the first query to get the time interval when the story was in the "Completed" state:
https://rally1.rallydev.com/analytics/v2.0/service/rally/workspace/1234/artifact/snapshot/query.js?find={"ObjectID":777,"ScheduleState": "Completed","_PreviousValues.ScheduleState": "In-Progress"}&fields=["ObjectID","_ValidFrom","_ValidTo","ScheduleState"]&hydrate=["ScheduleState"]
It may return more than one snapshot, and depending on what fields are fetched there may not be an indication what changed between those snapshots (e.g. in this case TaskStatus and TaskRemainingTotal) but in any case the earliest snapshot's _ValidFrom value, _ValidFrom: "2013-06-17T18:51:36.931Z" is the date you are looking for
Results:
[
{
_ValidFrom: "2013-06-17T18:51:36.931Z",
_ValidTo: "2013-06-17T18:51:44.382Z",
ObjectID: 12353154323,
ScheduleState: "Completed"
},
{
_ValidFrom: "2013-06-17T18:55:50.897Z",
_ValidTo: "2013-06-18T20:53:01.755Z",
ObjectID: 12353154323,
ScheduleState: "Completed"
}
]
If you are writing a code, you will get the _ValidFrom of the first element of the array of objects.

How to get more than 200 data from a data store

I would like to link TestCase TestFolder and TestSet.
To do so I start 2 WsapiDataStore queries one on TestFolder and one on TestSet.
Then I parse the data and get the matching TestCase.
Unfortunatly I have not found the way to get more than 200 elements for each queries or to
index the starting index of the queries.
The code I use for a WsapiDataStore query is
_GetTestSetStore : function(TestFolder, container) {
var TestSet_Store = Ext.create('Rally.data.WsapiDataStore', {
model : 'TestSet',
fetch : [ 'FormattedID', 'TestSet' ],
pageSize : PageSize,
autoLoad : true,
listeners : {
load : function(TestSet_Store, TestSet_Data, success) {
if (success) {
container._CleanStore(TestSet_Store, TestSet_Data, TestFolder, container);
} else {
alert('TestSet store query failed');
}
}
}
});
},
Could you help please
Rally.data.wsapi.Store has a config property limit.
You may set limit to Infinity, no quotes.
limit : Number
The total number or records to retrieve with the initial load Defaults to one page worth. To retrieve all records specify Infinity

Lookback API remove unauthorized snapshots

When I ran into issues with a query to the lbapi, I took a step back a created a very basic app with just the query in it that logged the results.
It looked something like this:
Deft.Chain.pipeline([
function() {
var dd = Ext.create('Deft.Deferred');
Ext.create('Rally.data.lookback.SnapshotStore', {
fetch : ['Parent', 'Feature'],
filters : [{
property : '__At',
value : 'current'
},{
property : '_TypeHierarchy',
value : 'HierarchicalRequirement'
}]
}).load({
params : {
compress : true,
removeUnauthorizedSnapshots : true
},
callback : function(store) {
console.log('store',store);
dd.resolve(store);
}
});
return dd.promise;
}
]).then({
success: function(records) {
console.log('records', records);
}
});
Strangely, if I added a filter like this:
{
property : 'Parent',
operator : '!=',
value : null
}
I got more results. I concluded that the removeUnauthorizedSnapshots must filter the results after they have all been gathered into a page of 20000 results, and thus this would be possible. Can anyone confirm this? Hopefully such confusion can be avoided in the future
You are correct.
removeUnauthorizedSnapshots filters the current pagesize set of results, which means it might actually return a page with 0 results in an extreme case when all results are or were once associated with projects that the user is not allowed to access.
I am not sure about the outcome when you got more results. Additional filter should only limit the number of results further, and I see further reduction when I use a similar code.
But I would like to suggest a syntax change for the filter on Parent property. Nulls are not storied in Lookback API at all, so any != null or == null queries are a little misleading. In your code it works, but in the case of Parent == null, it will return snapshots that don't have a Parent attribute, not just those that have a Parent attribute that is null. You may use exists true instead of != null
filters : [
{
property : 'Parent',
operator : 'exists',
value : true
},{
property : '__At',
value : 'current'
},{
property : '_TypeHierarchy',
value : 'HierarchicalRequirement'
}]

How to prevent Facet Terms from tokenizing

I am using Facet Terms to get all the unique values and their count for a field. And I am getting wrong results.
term: web
Count: 1191979
term: misc
Count: 1191979
term: passwd
Count: 1191979
term: etc
Count: 1191979
While the actual result should be:
term: WEB-MISC /etc/passwd
Count: 1191979
Here is my sample query:
{
"facets": {
"terms1": {
"terms": {
"field": "message"
}
}
}
}
If reindexing is an option, it would be the best to change mapping and mark this fields as not_analyzed
"your_field" : { "type": "string", "index" : "not_analyzed" }
You can use multi field type if keeping an analyzed version of the field is desired:
"your_field" : {
"type" : "multi_field",
"fields" : {
"your_field" : {"type" : "string", "index" : "analyzed"},
"untouched" : {"type" : "string", "index" : "not_analyzed"}
}
}
This way, you can continue using your_field in the queries, while running facet searches using your_field.untouched.
Alternatively, if this field is stored, you can use a script field facet instead:
"facets" : {
"term" : {
"terms" : {
"script_field" : "_fields.your_field.value"
}
}
}
As the last resort, if this field is not stored, but record source is stored in the index, you can try this:
"facets" : {
"term" : {
"terms" : {
"script_field" : "_source.your_field"
}
}
}
The first solution is the most efficient. The last solution is the least efficient and may take a lot of time on a large index.
Wow, I also got this same issue today while term aggregating in the recent elastic-search. After googling and some partial understanding, found how this geeky indexing works(which is very simple).
Queries can find only terms that actually exist in the inverted index
When you index the following string
"WEB-MISC /etc/passwd"
it will be passed to an analyzer. The analyzer might tokenize it into
"WEB", "MISC", "etc" and "passwd"
with its position details. And this tokens might filtered to lowercase such as
"web", "misc", "etc" and "passwd"
So, after indexing,the search query can see the above 4 only. not the complete word "WEB-MISC /etc/passwd". For your requirement the following are my options you can use
1.Change the Default Analyzer used by elasticsearch([link][1])
2.If it is not need, just TurnOff the analyzer by setting 'not_analyzed' for the fields you need
3.To convert the already indexed data searchable, re-indexing is the only option
I have briefly explained this problem and proposed two solutions here.
I have talked about multiple approaches here.
One is use of not_analyzed to preserve the string as it is. But then as it has the drawback of being case insensitive , a better approach would be use keyword tokenizer + lowercase filter