5

Ho un ambito un po 'complicato su un modelloNegate ActiveRecord interrogazione portata

class Contact < ActiveRecord::Base 
    scope :active,  -> { where(inactive: false) } 
    scope :groups,  -> { where(contact_type: 2308) } 
    scope :group_search, -> (query) do 
    active.groups.where("last_name LIKE '%' + ? + '%'", query) 
    end 
end 

A scopo di verifica, voglio fare in modo che tutti Contactsnon restituito da group_search sono esclusi per i motivi giusti.

Ma per ottenere tale elenco, devo caricare

Contact.all - Contact.group_search('query') 

che gestisce due query, restituisce un Array invece di un Relation, ed è più lento di quanto mi piacerebbe.

E dal momento che sto testando lo scope group_search, scrivendo lo un altro scope negativo è il tipo di errore. Preferirei solo fare qualcosa di simile:

Contact.merge.not(Contact.group_search('query')) 

per generare la seguente query SQL:

SELECT * 
FROM contacts 
WHERE NOT (contact_type = 2308 AND inactive = 0 AND last_name LIKE '%' + ? + '%') 

C'è un modo di fare questo?

+0

Un modo leggermente più veloce sarebbe: 'Contact.where.not (id: Contact.group_search ('query'). Pluck (: id))'. Ancora due query, ma restituirà una relazione e limiterà drasticamente una delle query. AFAIK, non c'è modo di negare l'ambito atm. – BroiSatse

+0

Sì ... anche la query stessa potrebbe potenzialmente diventare molto grande ... questa tabella ha qualcosa dell'ordine di 100k e gli ID sono UUID, quindi se voglio "non (qualcosa di molto comune)" potrei guardare a megabyte solo per la stringa di query. – PJSCopeland

risposta

7

Penso che quello che state cercando è chiamato negare la portata, si può provare il seguente:

conditions = Contact.group_search('query').where_values 
@contacts = Contact.where.not(conditions.reduce(:and)) 

Per questa soluzione per lavorare in Rails 4.x, è necessario fornire i valori nel campo di applicazione come array :

scope :groups, -> { where(contact_type: [2308]) } 

I'v anche trovato un modo accurato general implementation for negating the scopes, si può anche essere interessante.

+0

Grazie! Ma perché dovrei usare gli array? – PJSCopeland

+0

Sembra che dovrebbe funzionare perfettamente - sfortunatamente sembra che SQL Server stia diventando una cagna al riguardo: 'TinyTds :: Errore: un'espressione di tipo non booleano specificata in un contesto in cui è prevista una condizione, vicino a ')'. : EXEC sp_executesql N'SELECT [contatto]. * FROM [contatto] WHERE (NOT ([contact]. [Contact_type] = 2308 AND [contact]. [Inattivo] = 0 AND N''last_name LIKE '' ''% query % '' '' '')) '' – PJSCopeland

+0

@Patrick: Non so perché, ma Rails non è un argomento vincolante se i valori non sono forniti come array, sembra essere un bug noto che è apparso in Rails 4.2. Quale versione usi? – potashin