2009-07-09 25 views
9

Tôi đang cố gắng viết truy vấn GQL trả về N bản ghi ngẫu nhiên của một loại cụ thể. Việc triển khai hiện tại của tôi hoạt động nhưng yêu cầu N cuộc gọi đến kho dữ liệu. Tôi muốn làm cho nó 1 cuộc gọi đến kho dữ liệu nếu có thể.Truy vấn cho N bản ghi ngẫu nhiên trên kho dữ liệu Appengine

Tôi hiện đang chỉ định một số ngẫu nhiên cho mọi loại mà tôi đưa vào kho dữ liệu. Khi tôi truy vấn một bản ghi ngẫu nhiên, tôi tạo một số ngẫu nhiên khác và truy vấn cho các bản ghi> rand ORDER BY asc LIMIT 1.

Công trình này chỉ trả về 1 bản ghi vì vậy tôi cần thực hiện N truy vấn. Bất kỳ ý tưởng về cách thực hiện một truy vấn này? Cảm ơn.

+0

tôi đã tạo ra một vấn đề cho điều này, bạn có thể sao nó để giúp nó cố định: https://code.google.com/p/googleappengine/issues/detail?id=9044 –

Trả lời

5

"Dưới mui xe", một cuộc gọi truy vấn tìm kiếm duy nhất chỉ có thể trả lại một tập hợp các hàng liên tiếp từ một số chỉ mục. Đây là lý do tại sao một số truy vấn GQL, bao gồm bất kỳ việc sử dụng nào! =, Mở rộng đến nhiều lệnh gọi datastore.

N lựa chọn ngẫu nhiên đồng nhất độc lập không phải là (nói chung) liên tiếp trong bất kỳ chỉ mục nào.

QED.

Bạn có thể sử dụng memcache để lưu trữ các thực thể và giảm chi phí lấy N của chúng. Hoặc nếu bạn không nhớ các lựa chọn "ngẫu nhiên" ở gần nhau trong chỉ mục, hãy chọn một khối ngẫu nhiên được chọn (nói) 100 trong một truy vấn, sau đó chọn ngẫu nhiên N từ những truy vấn đó. Vì bạn có một trường đã được chọn ngẫu nhiên, nó sẽ không hiển nhiên ngay lập tức với một người ngoài mà N mục có liên quan. Ít nhất, không phải cho đến khi họ nhìn vào rất nhiều mẫu và nhận thấy rằng các mục A và Z không bao giờ xuất hiện trong cùng một nhóm, bởi vì chúng cách nhau hơn 100 chỉ số ngẫu nhiên. Và nếu hiệu suất cho phép, bạn có thể tái ngẫu nhiên các thực thể của bạn theo thời gian.

+0

Cảm ơn - Tôi thực sự cần kết quả ngẫu nhiên vì vậy tôi đoán tôi sẽ phải sử dụng nhiều cuộc gọi kho dữ liệu. Tôi sẽ cố gắng giảm thiểu N nhiều như tôi có thể đoán. – aloo

+0

điều này không đúng. cả [thao tác theo lô] (https://developers.google.com/appengine/docs/python/datastore/entities?hl=en#Batch_Operations) và ['IN'] (https://developers.google.com/ appengine/docs/python/datastore/query # Property_Filters) toán tử truy vấn có thể trả về các thực thể không liên tiếp. – ryan

+0

@ryan: giống với '! ='. Cả hai điều đó và 'IN' được triển khai dưới dạng một số truy vấn con giới hạn. Hàng loạt ops không thực sự có liên quan đến câu hỏi, nhưng có, đó là sự thật rằng một số hoạt động hành động trên các thực thể không tiếp giáp trong bất kỳ chỉ mục. Chỉ cần không tìm kiếm. –

3

Có vẻ như phương pháp duy nhất là bằng cách lưu trữ giá trị số nguyên ngẫu nhiên trong thuộc tính đặc biệt của mỗi thực thể và truy vấn trên đó. Điều này có thể được thực hiện khá tự động nếu bạn chỉ cần thêm một thuộc tính được khởi tạo tự động.

Thật không may điều này sẽ yêu cầu xử lý tất cả các thực thể một lần nếu kho dữ liệu của bạn đã được điền.

Thật kỳ lạ, tôi biết.

+0

Tôi nghĩ đây là một cách tiếp cận tuyệt vời, và phù hợp với mô hình NoSQL làm công việc viết thay vì đọc. Tất nhiên, điều này sẽ không hoàn toàn ngẫu nhiên - nếu bạn luôn luôn có N mục tuần tự trở lại sau đó một người dùng thỉnh thoảng sẽ thấy các bản ghi tương tự bên cạnh nhau. Nhưng điều đó có thể là ngẫu nhiên đủ cho OP. (Bạn cũng có thể tạo vài trăm - thậm chí hàng trăm thuộc tính với các số ngẫu nhiên khác nhau và xoay chỉ số bạn vẽ.) – npdoty

4

Bạn đang tìm kiếm sự cân bằng nào? Nếu bạn sẵn sàng để đưa ra một hit hiệu suất nhỏ trên chèn các thực thể này, bạn có thể tạo ra một giải pháp để có được N của họ rất nhanh chóng.

Đây là những gì bạn cần làm:

Khi bạn chèn Thực thể, hãy chỉ định khóa. Bạn muốn cung cấp chìa khóa cho các thực thể của bạn theo thứ tự, bắt đầu bằng 1 và đi lên từ đó. (Điều này sẽ yêu cầu một số nỗ lực, vì công cụ ứng dụng không có autoincrement() vì vậy bạn sẽ cần phải theo dõi id cuối cùng bạn đã sử dụng trong một số thực thể khác, hãy gọi nó là IdGenerator)

Bây giờ khi bạn cần N thực thể ngẫu nhiên, tạo ra N số ngẫu nhiên giữa 1 và bất kỳ id cuối cùng bạn tạo ra là (IdGenerator của bạn sẽ biết điều này). Sau đó bạn có thể thực hiện một đợt lấy bằng khóa bằng cách sử dụng các phím N, sẽ chỉ yêu cầu một chuyến đi đến kho dữ liệu, và sẽ nhanh hơn truy vấn, vì các khóa được thường nhanh hơn truy vấn, AFAIK.

Phương pháp này không đòi hỏi phải làm việc với một vài chi tiết gây phiền nhiễu:

  1. IdGenerator của bạn có thể trở thành một nút cổ chai nếu bạn đang chèn rất nhiều các mặt hàng này một cách nhanh chóng (hơn một vài giây), mà sẽ yêu cầu một số loại triển khai IdGenerator bị loại bỏ.Nếu tất cả dữ liệu này được tải trước hoặc không có khối lượng lớn, bạn sẽ dễ dàng.
  2. Bạn có thể thấy rằng một số Id không thực sự có một thực thể liên kết với nó nữa, bởi vì bạn đã xóa nó hoặc vì một put() không thành công ở đâu đó. Nếu điều này xảy ra, bạn phải lấy một thực thể ngẫu nhiên khác. (Nếu bạn muốn trở nên ưa thích và giảm tỷ lệ cược này, bạn có thể làm cho Id này có sẵn cho IdGenerator để tái sử dụng để "điền vào các lỗ")

Vì vậy, câu hỏi đặt ra là bạn cần bao nhiêu các mục so với mức độ thường xuyên bạn sẽ thêm và xóa chúng và liệu độ phức tạp thêm có đáng để tăng hiệu suất hay không.

+1

Bạn có thể thực hiện nhiều việc này bằng cách sử dụng số ID được tích hợp sẵn của App Engine - nếu bạn biết ID tối đa, bạn có thể chọn ngẫu nhiên một cách thống nhất. Một số sẽ không tồn tại, vì vậy hãy thử lại chúng với các id ngẫu nhiên mới, v.v. Nếu không gian ID của bạn dày đặc, điều này sẽ hoạt động tốt. –

+0

ngọt. Tôi không biết chúng ta có thể dựa vào việc đánh số được xây dựng để bắt đầu từ 1 và đi lên 1 từ 1 từ đó. –

+0

Bạn không thể - nhưng nó sẽ phân bổ theo khối, và miễn là các khối được sử dụng _mostly_, các lần thử lại của bạn phải đủ nhỏ để có thể quản lý được. –

0

Tôi vừa gặp sự cố tương tự. Tôi quyết định không gán ID cho các mục đã tồn tại của tôi trong kho dữ liệu và đã làm điều này, vì tôi đã có tổng số tiền từ một bộ đếm được phân loại.

Điều này chọn mục "đếm" từ mục "totalcount", được sắp xếp theo khóa.

# select $count from the complete set 
    numberlist = random.sample(range(0,totalcount),count) 
    numberlist.sort() 

    pagesize=1000 

    #initbuckets 
    buckets = [ [] for i in xrange(int(max(numberlist)/pagesize)+1) ] 
    for k in numberlist: 
     thisb = int(k/pagesize) 
     buckets[thisb].append(k-(thisb*pagesize)) 
    logging.debug("Numbers: %s. Buckets %s",numberlist,buckets) 

    #page through results. 

    result = [] 
    baseq = db.Query(MyEntries,keys_only=True).order("__key__") 
    for b,l in enumerate(buckets): 
     if len(l) > 0: 
      result += [ wq.fetch(limit=1,offset=e)[0] for e in l ] 

     if b < len(buckets)-1: # not the last bucket 
      lastkey = wq.fetch(1,pagesize-1)[0] 
      wq = baseq.filter("__key__ >",lastkey) 

Hãy coi chừng điều này với tôi có phần phức tạp và tôi vẫn chưa cho rằng tôi không có lỗi off-by-one hoặc off-by-x.

Và hãy cẩn thận rằng nếu đếm gần với tổng số tiền thì điều này có thể rất tốn kém. Và hãy cẩn thận rằng trên hàng triệu hàng, nó có thể không được thực hiện trong các ranh giới thời gian appengine.

1

Tôi đồng ý với câu trả lời từ Steve, không có cách nào để truy xuất N hàng ngẫu nhiên trong một truy vấn.

Tuy nhiên, ngay cả phương pháp truy xuất một thực thể đơn lẻ cũng thường không hoạt động sao cho tính khả thi của các kết quả trả về được phân bố đều. Xác suất trả về một thực thể cụ thể phụ thuộc vào khoảng trống của số được gán ngẫu nhiên và số ngẫu nhiên cao hơn tiếp theo. Ví dụ. nếu số ngẫu nhiên 1,2 và 10 đã được gán (và không có số nào trong số 3-9), thuật toán sẽ trả về "2" 8 lần thường xuyên hơn "1".

Tôi đã sửa lỗi này theo cách hơi khắt khe hơn một chút. Nếu ai đó quan tâm, tôi vui lòng chia sẻ

-1

Nếu tôi hiểu chính xác, bạn cần truy xuất N trường hợp ngẫu nhiên.

Thật dễ dàng. Chỉ cần thực hiện truy vấn bằng các phím. Và làm random.choice N lần trên kết quả danh sách các phím. Sau đó nhận kết quả bằng cách tìm nạp trên các phím.

keys = MyModel.all(keys_only=True) 

n = 5 # 5 random instance 

all_keys = list(keys) 
result_keys = [] 

for _ in range(0,n) 
    key = random.choice(all_keys) 
    all_keys.remove(key) 
    result_keys.append(key) 

# result_keys now contain 5 random keys. 
+0

Và nếu bạn có một triệu thực thể trong kho dữ liệu của bạn? Tải tất cả các khóa từ kho dữ liệu - có vẻ xấu ... – aloo

+0

@aloo nếu bạn có quá nhiều phiên bản, bạn có thể theo dõi tổng số chúng trong kho dữ liệu và memcache, sau đó bạn chỉ cần thực hiện 'random.choice' trên phạm vi số từ 0 đến tổng số. Và sau khi bạn chỉ lặp lại các khóa với chỉ mục, bạn đã tạo ra. Hoặc chỉ sử dụng giới hạn và bù đắp. –

Các vấn đề liên quan