lancer-shared 1.2.329 → 1.2.330
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.cjs.js
CHANGED
|
@@ -7826,6 +7826,7 @@ const SearchFieldsSchema = z.object({
|
|
|
7826
7826
|
exactPhrase: z.string().default(''),
|
|
7827
7827
|
titleSearch: z.string().default(''), // AND logic for titles
|
|
7828
7828
|
titleAny: z.string().default(''), // OR logic for titles
|
|
7829
|
+
titleNoneAny: z.string().default(''), // NOT logic for titles
|
|
7829
7830
|
skillsSearch: z.string().default(''),
|
|
7830
7831
|
});
|
|
7831
7832
|
class SearchQueryBuilder {
|
|
@@ -7897,6 +7898,14 @@ class SearchQueryBuilder {
|
|
|
7897
7898
|
parts.push(`(${titleParts.join(' OR ')})`);
|
|
7898
7899
|
}
|
|
7899
7900
|
}
|
|
7901
|
+
if (validatedFields.titleNoneAny.trim()) {
|
|
7902
|
+
const titleTerms = this.splitTerms(validatedFields.titleNoneAny);
|
|
7903
|
+
const titleNotParts = titleTerms.map((term) => {
|
|
7904
|
+
const isQuoted = term.startsWith('"') && term.endsWith('"');
|
|
7905
|
+
return isQuoted ? `NOT title:${term}` : `NOT title:"${term}"`;
|
|
7906
|
+
});
|
|
7907
|
+
parts.push(...titleNotParts);
|
|
7908
|
+
}
|
|
7900
7909
|
if (validatedFields.skillsSearch.trim()) {
|
|
7901
7910
|
const skillsTerms = this.splitTerms(validatedFields.skillsSearch);
|
|
7902
7911
|
if (skillsTerms.length === 1) {
|
|
@@ -7932,6 +7941,8 @@ class SearchQueryBuilder {
|
|
|
7932
7941
|
descriptions.push(`Title (all): ${validatedFields.titleSearch}`);
|
|
7933
7942
|
if (validatedFields.titleAny)
|
|
7934
7943
|
descriptions.push(`Title (any): ${validatedFields.titleAny}`);
|
|
7944
|
+
if (validatedFields.titleNoneAny)
|
|
7945
|
+
descriptions.push(`Title (excluding any): ${validatedFields.titleNoneAny}`);
|
|
7935
7946
|
if (validatedFields.skillsSearch)
|
|
7936
7947
|
descriptions.push(`Skills: ${validatedFields.skillsSearch}`);
|
|
7937
7948
|
return descriptions.length > 0 ? descriptions.join(' • ') : 'All results';
|
|
@@ -7961,6 +7972,7 @@ class SearchQueryBuilder {
|
|
|
7961
7972
|
exactPhrase: '',
|
|
7962
7973
|
titleSearch: '',
|
|
7963
7974
|
titleAny: '',
|
|
7975
|
+
titleNoneAny: '',
|
|
7964
7976
|
skillsSearch: '',
|
|
7965
7977
|
};
|
|
7966
7978
|
if (!query || query.trim() === '*') {
|
|
@@ -7993,7 +8005,35 @@ class SearchQueryBuilder {
|
|
|
7993
8005
|
// Remove the matched group from remaining query
|
|
7994
8006
|
remainingQuery = remainingQuery.replace(match[0], '').trim();
|
|
7995
8007
|
}
|
|
7996
|
-
// 2. THEN: Extract
|
|
8008
|
+
// 2. THEN: Extract NOT title groups like NOT (title:"a" OR title:"b")
|
|
8009
|
+
const titleNotGroupPattern = /NOT\s+\((title:"[^"]+"(?:\s+OR\s+title:"[^"]+")+?)\)/g;
|
|
8010
|
+
const titleNotGroupMatches = [...remainingQuery.matchAll(titleNotGroupPattern)];
|
|
8011
|
+
for (const match of titleNotGroupMatches) {
|
|
8012
|
+
const terms = match[1]
|
|
8013
|
+
.split(/\s+OR\s+/)
|
|
8014
|
+
.map((term) => term.replace(/^title:"([^"]+)"$/, '"$1"'))
|
|
8015
|
+
.filter(Boolean);
|
|
8016
|
+
result.titleNoneAny = result.titleNoneAny
|
|
8017
|
+
? `${result.titleNoneAny} ${terms.join(' ')}`
|
|
8018
|
+
: terms.join(' ');
|
|
8019
|
+
remainingQuery = remainingQuery.replace(match[0], '').trim();
|
|
8020
|
+
}
|
|
8021
|
+
// 3. Extract individual NOT title: terms before generic title extraction
|
|
8022
|
+
const titleNotPattern = /NOT\s+title:("([^"]+)"|([^\s)]+))/g;
|
|
8023
|
+
const titleNotMatches = [...remainingQuery.matchAll(titleNotPattern)];
|
|
8024
|
+
if (titleNotMatches.length > 0) {
|
|
8025
|
+
const terms = titleNotMatches.map((match) => {
|
|
8026
|
+
if (match[2]) {
|
|
8027
|
+
return `"${match[2]}"`;
|
|
8028
|
+
}
|
|
8029
|
+
return match[3];
|
|
8030
|
+
});
|
|
8031
|
+
result.titleNoneAny = result.titleNoneAny
|
|
8032
|
+
? `${result.titleNoneAny} ${terms.join(' ')}`
|
|
8033
|
+
: terms.join(' ');
|
|
8034
|
+
remainingQuery = remainingQuery.replace(titleNotPattern, '').trim();
|
|
8035
|
+
}
|
|
8036
|
+
// 4. THEN: Extract individual title: and skills: patterns
|
|
7997
8037
|
const fieldPatterns = [
|
|
7998
8038
|
{ field: 'titleSearch', pattern: /title:("([^"]+)"|([^\s)]+))/g },
|
|
7999
8039
|
{ field: 'skillsSearch', pattern: /skills:("([^"]+)"|([^\s)]+))/g },
|
|
@@ -8018,7 +8058,7 @@ class SearchQueryBuilder {
|
|
|
8018
8058
|
remainingQuery = remainingQuery.replace(pattern, '').trim();
|
|
8019
8059
|
}
|
|
8020
8060
|
}
|
|
8021
|
-
//
|
|
8061
|
+
// 5. Handle legacy grouped field searches like title:(term1 AND term2)
|
|
8022
8062
|
const groupedFieldPattern = /(title|skills):\(([^)]+)\)/g;
|
|
8023
8063
|
let groupMatch;
|
|
8024
8064
|
while ((groupMatch = groupedFieldPattern.exec(remainingQuery)) !== null) {
|
|
@@ -8046,7 +8086,7 @@ class SearchQueryBuilder {
|
|
|
8046
8086
|
}
|
|
8047
8087
|
remainingQuery = remainingQuery.replace(groupMatch[0], '').trim();
|
|
8048
8088
|
}
|
|
8049
|
-
//
|
|
8089
|
+
// 6. Extract grouped NOT expressions: NOT (term1 OR term2 OR ...)
|
|
8050
8090
|
const groupedNotPattern = /NOT\s+\(([^)]+(?:\s+OR\s+[^)]+)+)\)/g;
|
|
8051
8091
|
const groupedNotMatches = [...remainingQuery.matchAll(groupedNotPattern)];
|
|
8052
8092
|
if (groupedNotMatches.length > 0) {
|
|
@@ -8056,9 +8096,9 @@ class SearchQueryBuilder {
|
|
|
8056
8096
|
.replace(groupedNotMatches[0][0], '')
|
|
8057
8097
|
.trim();
|
|
8058
8098
|
}
|
|
8059
|
-
//
|
|
8099
|
+
// 7. Extract individual NOT terms (only if no grouped NOT was found)
|
|
8060
8100
|
if (!result.noneWords) {
|
|
8061
|
-
const notPattern = /NOT\s+("([^"]+)"|([^\s)]+))/g;
|
|
8101
|
+
const notPattern = /NOT\s+(?!title:)("([^"]+)"|([^\s)]+))/g;
|
|
8062
8102
|
const notMatches = [...remainingQuery.matchAll(notPattern)];
|
|
8063
8103
|
if (notMatches.length > 0) {
|
|
8064
8104
|
const noneTerms = notMatches.map((match) => {
|
|
@@ -8071,7 +8111,7 @@ class SearchQueryBuilder {
|
|
|
8071
8111
|
remainingQuery = remainingQuery.replace(notPattern, '').trim();
|
|
8072
8112
|
}
|
|
8073
8113
|
}
|
|
8074
|
-
//
|
|
8114
|
+
// 8. Process grouped expressions - DON'T clean up connectors first!
|
|
8075
8115
|
// Extract OR groups (anyWords) - PROCESS FIRST
|
|
8076
8116
|
const orGroupPattern = /\(([^)]+(?:\s+OR\s+[^)]+)+)\)/g;
|
|
8077
8117
|
const orMatches = [...remainingQuery.matchAll(orGroupPattern)];
|
|
@@ -8090,14 +8130,14 @@ class SearchQueryBuilder {
|
|
|
8090
8130
|
}
|
|
8091
8131
|
// NOW clean up connectors after group processing
|
|
8092
8132
|
remainingQuery = this.cleanupConnectors(remainingQuery);
|
|
8093
|
-
//
|
|
8133
|
+
// 9. Extract standalone quoted phrases (exactPhrase) - ONLY after all groups processed
|
|
8094
8134
|
const standaloneQuotePattern = /(?:^|\s)("([^"]+)")(?=\s|$)/g;
|
|
8095
8135
|
const quoteMatches = [...remainingQuery.matchAll(standaloneQuotePattern)];
|
|
8096
8136
|
if (quoteMatches.length > 0) {
|
|
8097
8137
|
result.exactPhrase = quoteMatches[0][2];
|
|
8098
8138
|
remainingQuery = remainingQuery.replace(quoteMatches[0][1], '').trim();
|
|
8099
8139
|
}
|
|
8100
|
-
//
|
|
8140
|
+
// 10. Handle remaining simple terms as allWords
|
|
8101
8141
|
if (remainingQuery) {
|
|
8102
8142
|
const remainingTerms = this.splitTermsWithQuotes(remainingQuery);
|
|
8103
8143
|
if (remainingTerms.length > 0) {
|