-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: first cut of explaining query result * feat: explainQueryResult tests * docs: update hubSearch guide with details about explainQueryResult * docs: add comment on export * docs: add docs for types * refactor: add async to failing test
- Loading branch information
Showing
13 changed files
with
1,412 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { IQuery, cloneObject, explainQueryResult } from "../src"; | ||
import { hubSearch } from "../src/search"; | ||
import Artifactory from "./helpers/Artifactory"; | ||
import config from "./helpers/config"; | ||
|
||
describe("Explain Query", () => { | ||
let factory: Artifactory; | ||
beforeAll(() => { | ||
factory = new Artifactory(config); | ||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; | ||
}); | ||
it("item in two groups", async () => { | ||
const ctxMgr = await factory.getContextManager("hubBasic", "admin"); | ||
|
||
const query: IQuery = { | ||
targetEntity: "item", | ||
filters: [ | ||
{ | ||
predicates: [ | ||
{ | ||
group: [ | ||
"9fef828e929d4f8e9fb5e5e3e174e9f6", // Explain Group 1 | ||
"9985c3ce1f0c4c39b065fc40a4548780", // Explain Group 2 | ||
], | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
const result = { | ||
id: "92ca9c12ee604b958303a52f3e0bbb6b", | ||
}; | ||
const explanation = await explainQueryResult( | ||
result, | ||
query, | ||
ctxMgr.context.requestOptions | ||
); | ||
}); | ||
it("item in two groups: catalog exclude one", async () => { | ||
const ctxMgr = await factory.getContextManager("hubBasic", "admin"); | ||
|
||
const query: IQuery = { | ||
targetEntity: "item", | ||
filters: [ | ||
{ | ||
predicates: [ | ||
{ | ||
group: { | ||
all: [ | ||
"9fef828e929d4f8e9fb5e5e3e174e9f6", // Explain Group 1 | ||
], | ||
not: [ | ||
"9985c3ce1f0c4c39b065fc40a4548780", // Explain Group 2 | ||
], | ||
}, | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
const result = { | ||
id: "92ca9c12ee604b958303a52f3e0bbb6b", | ||
}; | ||
const explanation = await explainQueryResult( | ||
result, | ||
query, | ||
ctxMgr.context.requestOptions | ||
); | ||
// debugger; | ||
}); | ||
it("two filters: item in two groups", async () => { | ||
const ctxMgr = await factory.getContextManager("hubBasic", "admin"); | ||
|
||
const query: IQuery = { | ||
targetEntity: "item", | ||
// Filters are ANDed together | ||
filters: [ | ||
{ | ||
predicates: [ | ||
{ | ||
group: [ | ||
"9fef828e929d4f8e9fb5e5e3e174e9f6", // Explain Group 1 | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
predicates: [ | ||
{ | ||
group: [ | ||
"9985c3ce1f0c4c39b065fc40a4548780", // Explain Group 2 | ||
], | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
// Add a term filter to get the specific result we want | ||
const searchQuery = cloneObject(query); | ||
searchQuery.filters.push({ | ||
predicates: [{ term: "Oak" }], | ||
}); | ||
// execute query | ||
const results = await hubSearch(query, { | ||
requestOptions: ctxMgr.context.hubRequestOptions, | ||
}); | ||
|
||
expect(results.results.length).toBe(1); | ||
|
||
const result = results.results[0]; | ||
|
||
expect(result.id).toBe("92ca9c12ee604b958303a52f3e0bbb6b"); | ||
// now get the explanation | ||
const explanation = await explainQueryResult( | ||
result, | ||
query, | ||
ctxMgr.context.requestOptions | ||
); | ||
// debugger; | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { IRequestOptions } from "@esri/arcgis-rest-request"; | ||
import { GenericResult, IFilterExplanation } from "../explainQueryResult"; | ||
import { IFilter } from "../types/IHubCatalog"; | ||
import { cloneObject } from "../../util"; | ||
import { explainPredicate } from "./explainPredicate"; | ||
|
||
/** | ||
* Generate an explanation if a specific result passes the filter's criteria | ||
* @param filter | ||
* @param result | ||
* @param requestOptions | ||
* @returns | ||
*/ | ||
|
||
export async function explainFilter( | ||
filter: IFilter, | ||
result: GenericResult, | ||
requestOptions: IRequestOptions | ||
): Promise<IFilterExplanation> { | ||
// setup return value | ||
const explanation: IFilterExplanation = { | ||
filter: cloneObject(filter), | ||
matched: false, | ||
reasons: [], | ||
}; | ||
// for each predicate, explain the match and return the explanation | ||
for (const predicate of filter.predicates) { | ||
const r = await explainPredicate(predicate, result, requestOptions); | ||
explanation.reasons.push(r); | ||
} | ||
// depending on the operation, we combine the predicate results differently | ||
if (filter.operation === "OR") { | ||
// if any of the predicates match, then the filter matches | ||
explanation.matched = explanation.reasons.some((r) => r.matched); | ||
} else { | ||
// filter.operation defaults to AND | ||
explanation.matched = explanation.reasons.every((r) => r.matched); | ||
} | ||
|
||
// return | ||
return explanation; | ||
} |
Oops, something went wrong.