/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define CFISH_USE_SHORT_NAMES #define LUCY_USE_SHORT_NAMES #define C_LUCY_SIMPLE #include "Lucy/Simple.h" #include "Clownfish/Err.h" #include "Clownfish/Hash.h" #include "Clownfish/HashIterator.h" #include "Clownfish/String.h" #include "Clownfish/Vector.h" #include "Lucy/Analysis/EasyAnalyzer.h" #include "Lucy/Document/Doc.h" #include "Lucy/Document/HitDoc.h" #include "Lucy/Index/Indexer.h" #include "Lucy/Index/PolyReader.h" #include "Lucy/Plan/FullTextType.h" #include "Lucy/Plan/Schema.h" #include "Lucy/Search/Hits.h" #include "Lucy/Search/IndexSearcher.h" #include "Lucy/Search/SortSpec.h" Simple* Simple_new(Obj *index, String *language) { Simple *self = (Simple*)Class_Make_Obj(SIMPLE); return Simple_init(self, index, language); } Simple* Simple_init(Simple *self, Obj *index, String *language) { SimpleIVARS *const ivars = Simple_IVARS(self); ivars->index = INCREF(index); ivars->language = Str_Clone(language); return self; } void Simple_Destroy_IMP(Simple *self) { SimpleIVARS *const ivars = Simple_IVARS(self); Simple_Finish_Indexing(self); DECREF(ivars->index); DECREF(ivars->language); DECREF(ivars->schema); DECREF(ivars->type); DECREF(ivars->indexer); DECREF(ivars->searcher); DECREF(ivars->hits); SUPER_DESTROY(self, SIMPLE); } static void S_create_indexer(Simple *self) { SimpleIVARS *const ivars = Simple_IVARS(self); // Trigger searcher refresh. DECREF(ivars->searcher); DECREF(ivars->hits); ivars->searcher = NULL; ivars->hits = NULL; // Get type and schema Schema *schema = NULL; FieldType *type = NULL; PolyReader *reader = PolyReader_open(ivars->index, NULL, NULL); Vector *seg_readers = PolyReader_Get_Seg_Readers(reader); if (Vec_Get_Size(seg_readers) == 0) { // Index is empty, create new schema and type. schema = Schema_new(); EasyAnalyzer *analyzer = EasyAnalyzer_new(ivars->language); type = (FieldType*)FullTextType_new((Analyzer*)analyzer); DECREF(analyzer); } else { // Get schema from reader. schema = (Schema*)INCREF(PolyReader_Get_Schema(reader)); Vector *fields = Schema_All_Fields(schema); String *field = (String*)CERTIFY(Vec_Fetch(fields, 0), STRING); type = (FieldType*)INCREF(Schema_Fetch_Type(schema, field)); DECREF(fields); } ivars->indexer = Indexer_new(schema, ivars->index, NULL, 0); ivars->schema = schema; ivars->type = type; DECREF(reader); } void Simple_Add_Doc_IMP(Simple *self, Doc *doc) { SimpleIVARS *const ivars = Simple_IVARS(self); if (!ivars->indexer) { S_create_indexer(self); } Vector *field_names = Doc_Field_Names(doc); for (size_t i = 0, max = Vec_Get_Size(field_names); i < max; i++) { String *field = (String*)Vec_Fetch(field_names, i); Schema_Spec_Field(ivars->schema, field, ivars->type); } Indexer_Add_Doc(ivars->indexer, doc, 1.0); DECREF(field_names); } uint32_t Simple_Search_IMP(Simple *self, String *query, uint32_t offset, uint32_t num_wanted, SortSpec *sort_spec) { SimpleIVARS *const ivars = Simple_IVARS(self); // Flush recent adds; lazily create searcher. Simple_Finish_Indexing(self); if (!ivars->searcher) { ivars->searcher = IxSearcher_new(ivars->index); } DECREF(ivars->hits); ivars->hits = IxSearcher_Hits(ivars->searcher, (Obj*)query, offset, num_wanted, sort_spec); return Hits_Total_Hits(ivars->hits); } HitDoc* Simple_Next_IMP(Simple *self) { SimpleIVARS *const ivars = Simple_IVARS(self); if (!ivars->hits) { return NULL; } // Get the hit, bail if hits are exhausted. HitDoc *doc = Hits_Next(ivars->hits); if (!doc) { DECREF(ivars->hits); ivars->hits = NULL; } return doc; } Indexer* Simple_Get_Indexer_IMP(Simple *self) { SimpleIVARS *const ivars = Simple_IVARS(self); if (!ivars->indexer) { S_create_indexer(self); } return ivars->indexer; } Hits* Simple_Get_Hits_IMP(Simple *self) { return Simple_IVARS(self)->hits; } void Simple_Finish_Indexing_IMP(Simple *self) { SimpleIVARS *const ivars = Simple_IVARS(self); // Don't bother to throw an error if index not modified. if (ivars->indexer) { Indexer_Commit(ivars->indexer); // Trigger searcher and indexer refresh. DECREF(ivars->schema); DECREF(ivars->type); DECREF(ivars->indexer); ivars->schema = NULL; ivars->type = NULL; ivars->indexer = NULL; } }