Просмотр исходного кода

Added search operators, minus and phrase search are available

Conflicts:
  lib/util/search.js
Norio Suzuki 9 лет назад
Родитель
Сommit
899340f64f
1 измененных файлов с 100 добавлено и 13 удалено
  1. 100 13
      lib/util/search.js

+ 100 - 13
lib/util/search.js

@@ -331,24 +331,70 @@ SearchClient.prototype.appendCriteriaForKeywordContains = function(query, keywor
   if (!query.body.query.bool) {
     query.body.query.bool = {};
   }
-
   if (!query.body.query.bool.must || !Array.isArray(query.body.query.must)) {
     query.body.query.bool.must = [];
   }
+  if (!query.body.query.bool.must_not || !Array.isArray(query.body.query.must_not)) {
+    query.body.query.bool.must_not = [];
+  }
 
-  query.body.query.bool.must.push({
-    multi_match: {
-      query: keyword,
-      // TODO: By user's i18n setting, change boost or search target fields
-      fields: [
-        "path_ja^2",
-        "body_ja",
-        // "path_en",
-        // "body_en",
-      ],
-      operator: "and"
+  var appendMultiMatchQuery = function(query, type, keywords) {
+    var target;
+    switch (type) {
+      case 'not_match':
+        target = query.body.query.bool.must_not;
+        break;
+      case 'match':
+      default:
+        target = query.body.query.bool.must;
     }
-  });
+
+    target.push({
+      multi_match: {
+        query: keywords.join(' '),
+        // TODO: By user's i18n setting, change boost or search target fields
+        fields: [
+          "path_ja^2",
+          "body_ja",
+          // "path_en",
+          // "body_en",
+        ],
+        operator: "and"
+      }
+    });
+
+    return query;
+  };
+
+  var parsedKeywords = this.getParsedKeywords(keyword);
+
+  if (parsedKeywords.match.length > 0) {
+    query = appendMultiMatchQuery(query, 'match', parsedKeywords.match);
+  }
+
+  if (parsedKeywords.not_match.length > 0) {
+    query = appendMultiMatchQuery(query, 'not_match', parsedKeywords.not_match);
+  }
+
+  if (parsedKeywords.phrase.length > 0) {
+    var phraseQueries = [];
+    parsedKeywords.phrase.forEach(function(phrase) {
+      phraseQueries.push({
+        multi_match: {
+          query: phrase, // each phrase is quoteted words
+          type: 'phrase',
+          fields: [ // Not use "*.ja" fields here, because we want to analyze (parse) search words
+            "path^2",
+            "body"
+          ],
+        }
+      });
+    });
+
+    // minus + phrase (ex. -"foo bar" ) is not support yet.
+
+    query.body.query.bool.must.push(phraseQueries);
+  }
 };
 
 SearchClient.prototype.appendCriteriaForPathFilter = function(query, path)
@@ -400,6 +446,47 @@ SearchClient.prototype.searchKeywordUnderPath = function(keyword, path, option)
   return this.search(query);
 };
 
+SearchClient.prototype.getParsedKeywords = function(keyword)
+{
+  var matchWords = [];
+  var notMatchWords = [];
+  var phraseWords = [];
+
+  keyword.trim();
+  keyword = keyword.replace(/\s+/g, ' ');
+
+  // First: Parse phrase keywords
+  var phraseRegExp = new RegExp(/("[^"]+")/g);
+  var phrases = keyword.match(phraseRegExp);
+  if (phrases !== null) {
+    keyword = keyword.replace(phraseRegExp, '');
+
+    phrases.forEach(function(phrase) {
+      phrase.trim();
+      phraseWords.push(phrase);
+    });
+  }
+
+  // Second: Parse other keywords (include minus keywords)
+  keyword.split(' ').forEach(function(word) {
+    if (word === '') {
+      return;
+    }
+
+    if (word.match(/^\-(.+)$/)) {
+      notMatchWords.push((RegExp.$1));
+    } else {
+      matchWords.push(word);
+    }
+  });
+
+  return {
+    match: matchWords,
+    not_match: notMatchWords,
+    phrase: phraseWords,
+  };
+}
+
 SearchClient.prototype.syncPageCreated = function(page, user)
 {
   debug('SearchClient.syncPageCreated', page.path);