我创建了一个Play 2.1 Scala应用程序。我不确定从Play应用程序调用Solr的最佳方法是什么:
Future
,但是这也会阻塞一个线程,对吗?我应该使用play.api.libs.ws.WS
库调用Solr并使用Plays JSON支持来提取结果(如下例所示)还是有更容易/更快的方式?
val solrQuery: Future[play.api.libs.ws.Response] = WS.url("http://localhost:8983/solr/collection1/select?q=id%3A123&wt=json").get()
答案 0 :(得分:2)
以下是我在我的项目中使用WS的方法:
val itselfNodeFuture = Statix.doParams( Statix.SolrSelectWSReq,
List(
"wt" -> "json",
"q" -> "*:*",
"fq" -> "node_type:collection",
"fq" -> "id:%d".format( nodeId),
"indent" -> "true",
"rows" -> "1",
"fl" -> "id,parent_id,title",
"fl" -> "date_created,date_about,date_modified")
).get()
//Use the first Await after the last future
val itselfJson = Await.result(
itselfNodeFuture, Duration("2 sec")).json
val mainRow = (itselfJson \ "response" \ "docs").as[ Seq[JsValue]]
val mainNodeParent = (mainRow(0) \ "parent_id").as[Long]
val mainNodeTitle = (mainRow(0) \ "title").as[String]
这是我使用的实用程序类,doParams
特别有用。
object Statix { //Noder must extend this
def SolrSelectWSReq = WS.url("http://127.0.0.1:8080/solr-store/collection1/select/")
def SolrUpdateWSReq = WS.url("http://127.0.0.1:8080/solr-store/collection1/update/json/")
def doParams(request: WS.WSRequestHolder, params: List[(String, String)]) = {
params.foldLeft( request){
(wsReq, tuple) => wsReq.withQueryString( tuple)}}
}
答案 1 :(得分:1)
您希望使用自己的Execution context将调用包装在Future中。这样调用可能会阻塞,但它会使用不同的线程池,而不会阻塞主应用程序。
事实上,这是面对阻塞或缓慢任务时的标准行为,例如向数据库发送查询或执行繁重的任务。
答案 2 :(得分:1)
最近遇到了这种需求,并没有找到任何有用的谷歌搜索。以下仅用于查询,但可以扩展。我假设你想留下SolrJ课程。 SolrQuery和QueryResponse非常容易使用。
所以要查询。您将要正常构建SolrQuery。对于" wt"供应" javabin"。这将为您提供SolrJ内部使用的压缩二进制格式的响应。
val sq = new SolrQuery()
sq.set("wt", "javabin")
...
您希望将SolrQuery变成WS理解的东西。 (我还没有添加所有的导入,因为大多数都很容易弄清楚[例如,通过你的IDE]。我所包含的那些可能不那么明显。)
import scala.collection.JavaConverters._
def solrQueryToForm(sq: SolrQuery): Map[String, Seq[String]] = {
sq.getParameterNames.asScala.foldLeft(Map.empty[String, Seq[String]]) {
case (m, n) =>
m + (n -> sq.getParams(n))
}
}
在我的商店中,我们使用默认的集合和处理程序(即" / select")但是您希望SolrQuery重写这些集合和/#34
def solrEndpoint(sq: SolrQuery): String = {
val coll = sq.get("collection", defaultCollection)
val hand = Option(sq.getRequestHandler).getOrElse(defaultHandler)
formSolrEndpoint(solrUrl, coll, hand)
}
def formSolrEndpoint(base: String, collection: String, handler: String): String = {
val sb = new StringBuilder(base)
if (sb.last != '/') sb.append('/')
sb.append(collection)
if (!handler.startsWith("/")) sb.append('/')
sb.append(handler)
sb.result()
}
您需要一些代码才能将WSResponse映射到QueryResponse
import com.ning.http.client.{Response => ACHResponse}
def wsResponseToQueryResponse(wsResponse: WSResponse)(implicit ctx: ExecutionContext): QueryResponse = {
val jbcUnmarshal = {
val rbis = wsResponse.underlying[ACHResponse].getResponseBodyAsStream
try {
new JavaBinCodec().unmarshal(rbis)
}
finally {
if (rbis != null)
rbis.close()
}
}
// p1: SolrJ pulls the same cast
// p2: We didn't use a SolrServer to chat with Solr so cannot provide it to QueryResponse
new QueryResponse(jbcUnmarshal.asInstanceOf[NamedList[Object]], null)
}
这为您提供了使用Play的异步WS服务调用Solr的所有功能。
def query(sq: SolrQuery)(implicit ctx: ExecutionContext): Future[QueryResponse] = {
val sqstar = sq.getCopy
sqstar.set("wt", "javabin")
WS.url(solrEndpoint(sqstar))
.post(solrQueryToForm(sqstar))
.map(wsResponseToQueryResponse)
}
由于Play现在将Web服务代码发布为独立jar,这意味着几乎任何项目都应该能够异步查询Solr。希望这很有用。