mirror of
https://github.com/zotero/zotero.git
synced 2026-08-28 05:25:31 +00:00
Fix negated Title/Creator/Year and Any Field advanced searches
These conditions expanded into an OR-group across their underlying fields, so a "does not contain"/"is not" operator matched almost every item: any item missing one of the fields satisfied that field's negated condition. Use an AND-group for negative operators, so the value must be absent from every field. https://forums.zotero.org/discussion/132835/
This commit is contained in:
parent
40974e493d
commit
e81e72dccf
2 changed files with 30 additions and 8 deletions
|
|
@ -1212,14 +1212,17 @@ Zotero.Search.prototype._buildQuery = async function () {
|
|||
// addCondition() so the expansion isn't saved in the search object.
|
||||
// Splice it in right after this condition so it's processed at the same
|
||||
// nesting depth -- "Any Field" inside a group stays in that group.
|
||||
// Always an OR-group: "Any Field" means "matches in any one of these
|
||||
// fields", correct whether the surrounding join mode is 'all' or 'any'
|
||||
// (an OR-group nested in an 'any' group flattens out).
|
||||
// For positive operators this is an OR-group ("matches in any one of
|
||||
// these fields"). For negative operators it must be an AND-group: "the
|
||||
// value appears in none of these fields" holds only when every field
|
||||
// fails to match (De Morgan), and an OR-group would match trivially for
|
||||
// any item missing one of the fields.
|
||||
let op = condition.operator;
|
||||
let val = condition.value;
|
||||
let joinMode = op == 'isNot' || op == 'doesNotContain' ? 'all' : 'any';
|
||||
conditionsToProcess.splice(conditionIndex + 1, 0,
|
||||
{ condition: 'groupStart', operator: 'true', value: '' },
|
||||
{ condition: 'joinMode', operator: 'any' },
|
||||
{ condition: 'joinMode', operator: joinMode },
|
||||
{ condition: 'field', operator: op, value: val },
|
||||
{ condition: 'tag', operator: op, value: val },
|
||||
{ condition: 'note', operator: op, value: val },
|
||||
|
|
@ -1234,13 +1237,16 @@ Zotero.Search.prototype._buildQuery = async function () {
|
|||
case 'titleCreatorYear': {
|
||||
// Expand to the same field set as 'quicksearch-titleCreatorYear' (without
|
||||
// key detection or the top-level-only restriction, which the result level
|
||||
// now handles). Spliced in after this condition like 'anyField' above, as
|
||||
// an OR-group so it matches in any one of these fields.
|
||||
// now handles). Spliced in after this condition like 'anyField' above.
|
||||
// An OR-group for positive operators; an AND-group for negative ones, so
|
||||
// "does not contain"/"is not" matches only when the value is absent from
|
||||
// every field (see 'anyField' above).
|
||||
let op = condition.operator;
|
||||
let val = condition.value;
|
||||
let joinMode = op == 'isNot' || op == 'doesNotContain' ? 'all' : 'any';
|
||||
conditionsToProcess.splice(conditionIndex + 1, 0,
|
||||
{ condition: 'groupStart', operator: 'true', value: '' },
|
||||
{ condition: 'joinMode', operator: 'any' },
|
||||
{ condition: 'joinMode', operator: joinMode },
|
||||
{ condition: 'title', operator: op, value: val },
|
||||
{ condition: 'publicationTitle', operator: op, value: val },
|
||||
{ condition: 'shortTitle', operator: op, value: val },
|
||||
|
|
|
|||
|
|
@ -1539,9 +1539,25 @@ describe("Zotero.Search", function () {
|
|||
s.libraryID = userLibraryID;
|
||||
s.addCondition('titleCreatorYear', 'contains', word);
|
||||
assert.sameMembers(await s.search(), [byTitle.id, byCreator.id]);
|
||||
|
||||
|
||||
await Zotero.Items.erase([byTitle.id, byCreator.id, byOther.id]);
|
||||
});
|
||||
it("should exclude items whose title contains the value for doesNotContain", async function () {
|
||||
var word = 'ztcy' + Zotero.Utilities.randomString();
|
||||
// Title contains the word -- must be excluded
|
||||
var withWord = await createDataObject('item', { title: 'a ' + word + ' b' });
|
||||
// Title lacks the word and has no publicationTitle/year/etc. -- must match
|
||||
var withoutWord = await createDataObject('item', { title: 'nothing here' });
|
||||
|
||||
var s = new Zotero.Search();
|
||||
s.libraryID = userLibraryID;
|
||||
s.addCondition('titleCreatorYear', 'doesNotContain', word);
|
||||
var matches = await s.search();
|
||||
assert.include(matches, withoutWord.id);
|
||||
assert.notInclude(matches, withWord.id);
|
||||
|
||||
await Zotero.Items.erase([withWord.id, withoutWord.id]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue