<commit_before>Algolia = require 'algoliasearch'
Promise = require 'bluebird'
globals = require 'globals'
kd = require 'kd'
KDNotificationView = kd.NotificationView
KDObject = kd.Object
remote = require('./remote').getInstance()
backoff = require 'backoff'
doXhrRequest = require './util/doXhrRequest'

module.exports = class SearchController extends KDObject

  constructor: (options, data) ->

    super options, data

    @indexes = {}

    @initAlgolia()


  initAlgolia: ->

    { appId } = globals.config.algolia

    @ready = no

    @fetchApiKey (err, apiKey) =>

      return kd.error err  if err

      @fetchGroupId (err, currentGroupId) =>

        return console.error "Could not fetch current group id: #{err}"  if err

        @algolia = new Algolia appId, apiKey
        @algolia.setSecurityTags(createSecurityTag(currentGroupId))
        @ready = yes


  createSecurityTag = (currentGroupId) ->
    userId = globals.userAccount.socialApiId
    return "(#{currentGroupId},account-#{userId})"


  fetchGroupId: (callback) ->

    currentGroupId = globals.currentGroup.socialApiChannelId

    # TODO in case of an error this currentGroup.socialApiChannelId is not set.
    # this is just a workaround, and if possible solve this issue and remove these lines
    return callback null, currentGroupId  if currentGroupId

    remote.api.JGroup.one {slug: globals.currentGroup.slug}, (err, group) ->

      return callback err  if err

      return callback null, group.socialApiChannelId  if group?.socialApiChannelId

      return callback {message: "socialApiChannelId not found"}


  fetchApiKey: (callback) ->
    bo = backoff.exponential
      initialDelay: 700
      maxDelay    : 15000

    bo.on 'fail', -> callback {message: "Authentication failed."}
    bo.failAfter 15

    bo.on 'ready', -> bo.backoff()

    requestFn = ->
      doXhrRequest {endPoint: "/api/social/search-key", type: "GET"}, (err, res) ->
        if err
          kd.warn "Could not fetch search api key: #{err.message}"
          return

        unless res?.apiKey
          kd.warn "Empty search api key response"
          return

        bo.reset()

        callback null, res.apiKey

    bo.on 'backoff', requestFn

    bo.backoff()


  search: (indexName, seed, options) ->

    new Promise (resolve, reject) =>

      return reject new Error 'Search not ready yet'  unless @ready

      return reject new Error 'Illegal input'  if seed is ''

      index = @getIndex "#{ indexName }#{ globals.config.algolia.indexSuffix }"
      index.search seed, (success, results) ->

        return reject new Error "Couldn't search algolia"  unless success
        return resolve results.hits ? []

      , options


  searchAccountsMongo: (seed) ->
    val = seed.replace /^@/, ''

    query =
      'profile.nickname': val

    remote.api.JAccount.one query
      .then (it) -> [it]

  searchAccounts: (seed, options = {}) ->

    opts =
      hitsPerPage                  : 10
      restrictSearchableAttributes : [ "nick" ]

    opts = kd.utils.extend opt, options

    seed = seed.replace /[^-\w]/g, ''

    @search 'accounts', seed, opts
      .then (data) ->
        throw new Error "No data!" if data.length is 0
        return data
      .map ({ objectID }) -> remote.cacheableAsync 'JAccount', objectID
      .catch (err) =>
        console.warn 'algolia strategy failed; trying mongo'
        console.warn err

        return @searchAccountsMongo seed
      .filter Boolean

  searchTopics: (seed) ->
    @search 'topics', seed

  searchChannel: (seed, channelId, options = {}) ->
    options.tagFilters = (options.tagFilters ? []).concat channelId

    @search 'messages', seed, options
      .map ({ objectID: id }) ->
        new Promise (resolve) ->
          kd.singletons.socialapi.message.byId { id }, (err, message) -><commit_msg>client: fix typo<commit_after>Algolia = require 'algoliasearch'
Promise = require 'bluebird'
globals = require 'globals'
kd = require 'kd'
KDNotificationView = kd.NotificationView
KDObject = kd.Object
remote = require('./remote').getInstance()
backoff = require 'backoff'
doXhrRequest = require './util/doXhrRequest'

module.exports = class SearchController extends KDObject

  constructor: (options, data) ->

    super options, data

    @indexes = {}

    @initAlgolia()


  initAlgolia: ->

    { appId } = globals.config.algolia

    @ready = no

    @fetchApiKey (err, apiKey) =>

      return kd.error err  if err

      @fetchGroupId (err, currentGroupId) =>

        return console.error "Could not fetch current group id: #{err}"  if err

        @algolia = new Algolia appId, apiKey
        @algolia.setSecurityTags(createSecurityTag(currentGroupId))
        @ready = yes


  createSecurityTag = (currentGroupId) ->
    userId = globals.userAccount.socialApiId
    return "(#{currentGroupId},account-#{userId})"


  fetchGroupId: (callback) ->

    currentGroupId = globals.currentGroup.socialApiChannelId

    # TODO in case of an error this currentGroup.socialApiChannelId is not set.
    # this is just a workaround, and if possible solve this issue and remove these lines
    return callback null, currentGroupId  if currentGroupId

    remote.api.JGroup.one {slug: globals.currentGroup.slug}, (err, group) ->

      return callback err  if err

      return callback null, group.socialApiChannelId  if group?.socialApiChannelId

      return callback {message: "socialApiChannelId not found"}


  fetchApiKey: (callback) ->
    bo = backoff.exponential
      initialDelay: 700
      maxDelay    : 15000

    bo.on 'fail', -> callback {message: "Authentication failed."}
    bo.failAfter 15

    bo.on 'ready', -> bo.backoff()

    requestFn = ->
      doXhrRequest {endPoint: "/api/social/search-key", type: "GET"}, (err, res) ->
        if err
          kd.warn "Could not fetch search api key: #{err.message}"
          return

        unless res?.apiKey
          kd.warn "Empty search api key response"
          return

        bo.reset()

        callback null, res.apiKey

    bo.on 'backoff', requestFn

    bo.backoff()


  search: (indexName, seed, options) ->

    new Promise (resolve, reject) =>

      return reject new Error 'Search not ready yet'  unless @ready

      return reject new Error 'Illegal input'  if seed is ''

      index = @getIndex "#{ indexName }#{ globals.config.algolia.indexSuffix }"
      index.search seed, (success, results) ->

        return reject new Error "Couldn't search algolia"  unless success
        return resolve results.hits ? []

      , options


  searchAccountsMongo: (seed) ->
    val = seed.replace /^@/, ''

    query =
      'profile.nickname': val

    remote.api.JAccount.one query
      .then (it) -> [it]

  searchAccounts: (seed, options = {}) ->

    opts =
      hitsPerPage                  : 10
      restrictSearchableAttributes : [ "nick" ]

    opts = kd.utils.extend opts, options

    seed = seed.replace /[^-\w]/g, ''

    @search 'accounts', seed, opts
      .then (data) ->
        throw new Error "No data!" if data.length is 0
        return data
      .map ({ objectID }) -> remote.cacheableAsync 'JAccount', objectID
      .catch (err) =>
        console.warn 'algolia strategy failed; trying mongo'
        console.warn err

        return @searchAccountsMongo seed
      .filter Boolean

  searchTopics: (seed) ->
    @search 'topics', seed

  searchChannel: (seed, channelId, options = {}) ->
    options.tagFilters = (options.tagFilters ? []).concat channelId

    @search 'messages', seed, options
      .map ({ objectID: id }) ->
        new Promise (resolve) ->
          kd.singletons.socialapi.message.byId { id }, (err, message) ->
{'fraction_non_alphanumeric': 0.11268890046899427, 'fraction_numerical': 0.0033871808233454925, 'mean_word_length': 2.6366650876361915, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '916080', 'n_tokens_mistral': 2353, 'n_tokens_neox': 2230, 'n_words': 662}