В нашей прошлой статье мы говорим о косинусном подобии. Теперь мы собираемся реализовать это в Ember.js. Если вы не слышали об этом, это веб-фреймворк JavaScript с открытым исходным кодом, основанный на шаблоне Model-view-viewmodel (MVVM). Это позволяет разработчикам создавать масштабируемые одностраничные веб-приложения, включая в структуру общие идиомы и лучшие практики.

Давайте начнем !

Давайте создадим FAQ с именем модели, который имеет свойство вопросов и ответов, используя ember cli.

$ ember g model faq question:string answer:string

Давайте изменим нашу модель и используем данные фикстуры.

import DS from 'ember-data';
let faq =  = DS.Model.extend({
  question: DS.attr('string'),
  answer: DS.attr('string')
});
faq.reopenClass({
  FIXTURES: [
    {
    id:1,
    question: 'How to log in ?',
    answer:'provide your username and password and click login'
  },
  {
    id:2,
    question:'How to Reset Password?',
    answer:'Click on the “Forgot password?” '
  }
]
});
export default faq;

Далее нам нужно написать код, который вычисляет косинусное сходство. Здесь мы вычисляем косинусное сходство. Вы можете сохранить его как cosine-sim.js в файле utils.

function termFreqMap(str) {
 var words = str.split(' ');
 var termFreq = {};
 words.forEach(function(w) {
  termFreq[w] = (termFreq[w] || 0) + 1;
 });
 return termFreq;
}
function addKeysToDict(map, dict) {
 for (var key in map) {
  dict[key] = true;
 }
}
function termFreqMapToVector(map, dict) {
 var termFreqVector = [];
 for (var term in dict) {
  termFreqVector.push(map[term] || 0);
 }
 return termFreqVector;
}
function vecDotProduct(vecA, vecB) {
 var product = 0;
 for (var i = 0; i < vecA.length; i++) {
  product += vecA[i] * vecB[i];
 }
 return product;
}
function vecMagnitude(vec) {
 var sum = 0;
 for (var i = 0; i < vec.length; i++) {
  sum += vec[i] * vec[i];
 }
 return Math.sqrt(sum);
}
function cosineSimilarity(vecA, vecB) {
 return vecDotProduct(vecA, vecB) / (vecMagnitude(vecA) * vecMagnitude(vecB));
}
export default function cosineSim(strA, strB) {
 var termFreqA = termFreqMap(strA);
 var termFreqB = termFreqMap(strB);
var dict = {};
 addKeysToDict(termFreqA, dict);
 addKeysToDict(termFreqB, dict);
var termFreqVecA = termFreqMapToVector(termFreqA, dict);
 var termFreqVecB = termFreqMapToVector(termFreqB, dict);
return cosineSimilarity(termFreqVecA, termFreqVecB);
}

Теперь нам нужно импортировать его в нашу модель, чтобы мы могли рассчитать частоту появления. Сначала мы установим оценку как ноль по умолчанию, а затем вычислим ее с помощью функции computeScore.

.....
import cosineSim from 'demo-app/utils/cosine-sim';
let faq = DS.Model.extend({
  question: DS.attr('string'),
  answer: DS.attr('string'),
  score: DS.attr('number', {defaultValue: 0}),
  computeScore: function(term){
    let _this = this;
    return new Promise(function(resolve, reject){
      let sc = cosineSim(term, _this.get('question'));
      _this.set('score', sc);
      resolve('score found');
    });
  },
});
.....

computeScore вычисляет оценку и устанавливает значение score, которое является результатом подобия косинуса .