Rails找到父有两个具有特定属性的孩子

时间:2017-07-03 16:06:12

标签: ruby-on-rails ruby

我的数据库中有超过100,000个对象用于不同的产品。每个产品有4-6个变体。因此,通过遍历所有内容来懒惰地编辑大量数据并不容易。因此,我只想获得我需要的确切产品数量。

到目前为止,我可以获得所有具有大小属性​​为“SM”的Variant的产品。

挂起的是,所有产品都具有尺寸为“MD”和“SM”的Variant。

这是我使用的代码Product.joins(:variants).where('variants.size = ?', 'SM')

我尝试向其添加.where('variants.size = ?', 'MD'),但这确实有效。

3 个答案:

答案 0 :(得分:3)

这个怎么样

Product.where(
 id: Variant.select(:product_id)
     .where(size: 'SM')
).where(id: Variant.select(:product_id)
    .where(size: 'MD')
)

这应该产生类似于

的东西
SELECT products.* 
FROM products 
WHERE products.id IN (SELECT 
                       variants.product_id 
                      FROM variants 
                      WHERE size = 'SM') 
AND products.id IN (SELECT 
                       variants.product_id 
                      FROM variants 
                      WHERE size = 'MD') 

因此产品ID必须在两个列表中才能被选中。

此外,这也应该有效(不是100%确定)

Product.where(id: Product.joins(:variants)
                 .where(variants: {size: ['SM', 'MD']})
                 .group(:id)
                 .having('COUNT(*) = 2').select(:id)

应生成类似

的内容
SELECT products.*
FROM products
WHERE 
 products.id IN ( SELECT products.id
                  FROM products
                  INNER JOIN variants 
                   ON variants.product_id = products.id
                  WHERE 
                    variants.size IN ('SM','MD')
                  GROUP BY 
                    products.id
                  HAVING 
                    Count(*) = 2  

还有一个选择

p_table = Products.arel_table
v_table = Variant.arel_table

sm_table = p_table.join(v_table)
           .on(v_table[:product_id].eq(p_table.[:id])
                .and(v_table[:size].eq('SM'))
           )
md_table = p_table.join(v_table)
           .on(v_table[:product_id].eq(p_table.[:id])
                 .and(v_table[:size].eq('MD'))
           )

Product.joins(sm_table.join_sources).joins(md_table.join_sources)

SQL

SELECT products.*
FROM products
INNER JOIN variants on variants.product_id = products.id
  AND variants.size = 'SM'
INNER JOIN variants on variants.product_id = products.id
  AND variants.size = 'MD'

由于INNER JOIN

,这两个联接应该强制执行小型和中型

答案 1 :(得分:1)

恕我直言,你需要使用更多的SQL而不是Rails魔法来构建类似的数据库查询。

package com.rishav.avro;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.LinkedHashMap;

import org.apache.avro.Schema;
import org.apache.avro.file.DataFileReader;
import org.apache.avro.file.DataFileWriter;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericDatumWriter;
import org.apache.avro.generic.GenericRecord;
//import org.apache.avro.io.BinaryDecoder;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DatumWriter;


import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.json.simple.JSONObject;



public class AvroExampleWithoutCodeGeneration {
    public void serialize() throws JsonParseException, JsonProcessingException, IOException {

        InputStream in = new FileInputStream("StudentActivity.json");

        // create a schema
        Schema schema = new Schema.Parser().parse(new File("StudentActivity.avsc"));**// THIS IS LINE 36**
        // create a record to hold json
        GenericRecord AvroRec = new GenericData.Record(schema);
        // create a record to hold course_details 
        GenericRecord CourseRec = new GenericData.Record(schema.getField("course_details").schema());
        // this file will have AVro output data
        File AvroFile = new File("resources/StudentActivity.avro");
        // Create a writer to serialize the record
        DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(schema);              
        DataFileWriter<GenericRecord> dataFileWriter = new DataFileWriter<GenericRecord>(datumWriter);

        dataFileWriter.create(schema, AvroFile);

        // iterate over JSONs present in input file and write to Avro output file
        ObjectMapper mapper = new ObjectMapper();
        Iterator it= (Iterator) mapper.readValue(new JsonFactory().createJsonParser(in), JSONObject.class); 
        while (it.hasNext())
        {
        //for (Iterator it = mapper.readValues(new JsonFactory().createJsonParser(in), JSONObject.class); it.hasNext();) {

            JSONObject JsonRec = (JSONObject) it.next();
            AvroRec.put("id", JsonRec.get("id"));
            AvroRec.put("student_id", JsonRec.get("student_id"));
            AvroRec.put("university_id", JsonRec.get("university_id"));

            LinkedHashMap CourseDetails = (LinkedHashMap) JsonRec.get("course_details");
            CourseRec.put("course_id", CourseDetails.get("course_id"));
            CourseRec.put("enroll_date", CourseDetails.get("enroll_date"));
            CourseRec.put("verb", CourseDetails.get("verb"));
            CourseRec.put("result_score", CourseDetails.get("result_score"));

            AvroRec.put("course_details", CourseRec);

            dataFileWriter.append(AvroRec);
        }  // end of for loop

        in.close();
        dataFileWriter.close();

    } // end of serialize method

    public void deserialize () throws IOException {
        // create a schema
        Schema schema = new Schema.Parser().parse(new File("resources/StudentActivity.avsc"));
        // create a record using schema
        GenericRecord AvroRec = new GenericData.Record(schema);
        File AvroFile = new File("resources/StudentActivity.avro");
        DatumReader<GenericRecord> datumReader = new GenericDatumReader<GenericRecord>(schema);
        DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(AvroFile, datumReader);
        System.out.println("Deserialized data is :");
        while (dataFileReader.hasNext()) {
            AvroRec = dataFileReader.next(AvroRec);
            System.out.println(AvroRec);
        }
    }

    public static void main(String[] args) throws JsonParseException, JsonProcessingException, IOException {
        AvroExampleWithoutCodeGeneration AvroEx = new AvroExampleWithoutCodeGeneration();
        AvroEx.serialize();
        AvroEx.deserialize();
    }

}

或简化 - 正如@engineersmnky建议:

Product
  .joins('INNER JOIN variants as sm_vs ON sm_vs.product_id = products.id')
  .joins('INNER JOIN variants as md_vs ON md_vs.product_id = products.id')
  .where(sm_vs: { size: 'SM' })
  .where(md_vs: { size: 'MD' })

两个查询基本相同。只需选择您喜欢的版本。

答案 2 :(得分:0)

Product.joins(:variants).where(&#39; variants.size =?OR variants.size =?&#39;,&#39; SM&#39;,&#39; MD&#39; )